Issues Creating Records with FsCheck - f#

This question is a follow-up to an earlier question on using FsCheck to generate records. The original question was answered with a well composed example solution. However, prior to the answer being shared, I attempted to create a generator which is included below. Unfortunately, the generated records of type QueryRequest = {Symbol: string; StartDate: DateTime; EndDate: DateTime} have the following issues:
Missing symbol
Start dates earlier than January 1, 2000
End dates later than January 1, 2019
Original:
{ Symbol = ""
StartDate = 8/9/2057 4:07:10 AM
EndDate = 10/14/2013 6:15:32 PM }
Shrunk:
{ Symbol = ""
StartDate = 8/9/2057 12:00:00 AM
EndDate = 10/14/2013 12:00:00 AM }
Since I am still in the process of becoming familiar with F#, I would appreciate suggestions/feedback on: how to address the aforementioned issues, and opportunities to improve the code in terms of structure, composition, etc.
namespace Parser
module DataGenerators =
open System
open FsCheck
type QueryRequest = {Symbol: string; StartDate: DateTime; EndDate: DateTime}
type Tweet =
static member GenerateRecords (year, month, day, symbol) =
try
let startDate = DateTime (year, month, day)
let endDate = startDate.AddDays 1.0
Some {Symbol = symbol; StartDate = startDate; EndDate = endDate}
with
| :? ArgumentOutOfRangeException -> None
static member Combine (years: int list) (months: int list) (days: int list) (symbols: string list) =
let rec loop acc years months days symbols =
match years, months, days, symbols with
| [], [], [], [] -> acc
| year :: years, month :: months, day :: days, symbol :: symbols -> loop ((year, month, day, symbol) :: acc) years months days symbols
| _, _, _, _ -> acc
loop [] years months days symbols
static member Generate () =
let years = Gen.choose (2000, 2019) |> Gen.sample 0 10
let months = Gen.choose (1, 12) |> Gen.sample 0 10
let days = Gen.choose(1, 31) |> Gen.sample 0 10
let symbols = Gen.elements ["ORCL"; "IBM"; "AAPL"; "GOOGL"] |> Gen.sample 0 10
Tweet.Combine years months days symbols
|> List.map Tweet.GenerateRecords
|> List.fold (fun acc r -> match r with Some q -> q :: acc | None -> acc) []

I cannot reproduce your issue, the following yields true for 1000s of executions:
Tweet.Generate()
|> List.forall (fun q ->
q.StartDate <= q.EndDate &&
q.StartDate >= DateTime(2000, 1, 1) &&
q.EndDate <= DateTime(2019, 12, 31) &&
["ORCL"; "IBM"; "AAPL"; "GOOGL"] |> List.contains q.Symbol)
However, you can simplify Tweet like so:
type Tweet =
static member GenerateRecords ((year, month, day), symbol) =
try
let startDate = DateTime (year, month, day)
let endDate = startDate.AddDays 1.0
Some {Symbol = symbol; StartDate = startDate; EndDate = endDate}
with
| :? ArgumentOutOfRangeException -> None
static member Generate () =
let years = Gen.choose (2000, 2019) |> Gen.sample 0 10
let months = Gen.choose (1, 12) |> Gen.sample 0 10
let days = Gen.choose(1, 31) |> Gen.sample 0 10
let symbols = Gen.elements ["ORCL"; "IBM"; "AAPL"; "GOOGL"] |> Gen.sample 0 10
let dates = List.zip3 years months days
List.zip dates symbols
|> List.choose Tweet.GenerateRecords

Related

Imperative to Functional

I have been doing a CodeWars exercise which can also be seen at dev.to.
The essence of it is:
There is a line for the self-checkout machines at the supermarket. Your challenge is to write a function that calculates the total amount of time required for the rest of the customers to check out!
INPUT
customers : an array of positive integers representing the line. Each integer represents a customer, and its value is the amount of time they require to check out.
n : a positive integer, the number of checkout tills.
RULES
There is only one line serving many machines, and
The order of the line never changes, and
The front person in the line (i.e. the first element in the array/list) proceeds to a machine as soon as it becomes free.
OUTPUT
The function should return an integer, the total time required.
The answer I came up with works - but it is highly imperative.
open System.Collections.Generic
open System.Linq
let getQueueTime (customerArray: int list) n =
let mutable d = new Dictionary<string,int>()
for i in 1..n do
d.Add(sprintf "Line%d" <| i, 0)
let getNextAvailableSupermarketLineName(d:Dictionary<string,int>) =
let mutable lowestValue = -1
let mutable lineName = ""
for myLineName in d.Keys do
let myValue = d.Item(myLineName)
if lowestValue = -1 || myValue <= lowestValue then
lowestValue <- myValue
lineName <- myLineName
lineName
for x in customerArray do
let lineName = getNextAvailableSupermarketLineName d
let lineTotal = d.Item(lineName)
d.Item(lineName) <- lineTotal + x
d.Values.Max()
So my question is ... is this OK F# code or should it be written in a functional way? And if the latter, how? (I started off trying to do it functionally but didn't get anywhere).
is this OK F# code or should it be written in a functional way?
That's a subjective question, so can't be answered. I'm assuming, however, that since you're doing an exercise, it's in order to learn. Learning functional programming takes years for most people (it did for me), but F# is a great language because it enables you learn gradually.
You can, however, simplify the algorithm. Think of a till as a number. The number represents the instant it's ready. At the beginning, you initialise them all to 0:
let tills = List.replicate n 0
where n is the number of tills. At the beginning, they're all ready at time 0. If, for example, n is 3, the tills are:
> List.replicate 3 0;;
val it : int list = [0; 0; 0]
Now you consider the next customer in the line. For each customer, you have to pick a till. You pick the one that is available first, i.e. with the lowest number. Then you need to 'update' the list of counters.
In order to do that, you'll need a function to 'update' a list at a particular index, which isn't part of the base library. You can define it yourself, however:
module List =
let set idx v = List.mapi (fun i x -> if i = idx then v else x)
For example, if you want to 'update' the second element to 3, you can do it like this:
> List.replicate 3 0 |> List.set 1 3;;
val it : int list = [0; 3; 0]
Now you can write a function that updates the set of tills given their current state and a customer (represented by a duration, which is also a number).
let next tills customer =
let earliestTime = List.min tills
let idx = List.findIndex (fun c -> earliestTime = c) tills
List.set idx (earliestTime + customer) tills
First, the next function finds the earliestTime in tills by using List.min. Then it finds the index of that value. Finally, it 'updates' that till by adding its current state to the customer duration.
Imagine that you have two tills and the customers [2;3;10]:
> List.replicate 2 0;;
val it : int list = [0; 0]
> List.replicate 2 0 |> fun tills -> next tills 2;;
val it : int list = [2; 0]
> List.replicate 2 0 |> fun tills -> next tills 2 |> fun tills -> next tills 3;;
val it : int list = [2; 3]
> List.replicate 2 0 |> fun tills -> next tills 2 |> fun tills -> next tills 3
|> fun tills -> next tills 10;;
val it : int list = [12; 3]
You'll notice that you can keep calling the next function for all the customers in the line. That's called a fold. This gives you the final state of the tills. The final step is to return the value of the till with the highest value, because that represents the time it finished. The overall function, then, is:
let queueTime line n =
let next tills customer =
let earliestTime = List.min tills
let idx = List.findIndex (fun c -> earliestTime = c) tills
List.set idx (earliestTime + customer) tills
let tills = List.replicate n 0
let finalState = List.fold next tills line
List.max finalState
Here's some examples, taken from the original exercise:
> queueTime [5;3;4] 1;;
val it : int = 12
> queueTime [10;2;3;3] 2;;
val it : int = 10
> queueTime [2;3;10] 2;;
val it : int = 12
This solution is based entirely on immutable data, and all functions are pure, so that's a functional solution.
Here is a version that resembles your version, with all the mutability removed:
let getQueueTime (customerArray: int list) n =
let updateWith f key map =
let v = Map.find key map
map |> Map.add key (f v)
let initialLines = [1..n] |> List.map (fun i -> sprintf "Line%d" i, 0) |> Map.ofList
let getNextAvailableSupermarketLineName(d:Map<string,int>) =
let lowestLine = d |> Seq.minBy (fun l -> l.Value)
lowestLine.Key
let lines =
customerArray
|> List.fold (fun linesState x ->
let lineName = getNextAvailableSupermarketLineName linesState
linesState |> updateWith (fun l -> l + x) lineName) initialLines
lines |> Seq.map (fun l -> l.Value) |> Seq.max
getQueueTime [5;3;4] 1 |> printfn "%i"
Those loops with mutable "outer state" can be swapped for either recursive functions or folds/reduce, here I suspect recursive functions would be nicer.
I've swapped out Dictionary for the immutable Map, but it feels like more trouble than it's worth here.
Update - here is a compromise solution I think reads well:
let getQueueTime (customerArray: int list) n =
let d = [1..n] |> List.map (fun i -> sprintf "Line%d" i, 0) |> dict
let getNextAvailableSupermarketLineName(d:IDictionary<string,int>) =
let lowestLine = d |> Seq.minBy (fun l -> l.Value)
lowestLine.Key
customerArray
|> List.iter (fun x ->
let lineName = getNextAvailableSupermarketLineName d
d.Item(lineName) <- d.Item(lineName) + 1)
d.Values |> Seq.max
getQueueTime [5;3;4] 1 |> printfn "%i"
I believe there is a more natural functional solution if you approach it freshly, but I wanted to evolve your current solution.
This is less an attempt at answering than an extended comment on Mark Seemann's otherwise excellent answer. If we do not restrict ourselves to standard library functions, the slightly cumbersome determination of the index with List.findIndex can be avoided. Instead, we may devise a function that replaces the first occurrence of a value in a list with a new value.
The implementation of our bespoke List.replace involves recursion, with an accumulator to hold the values before we encounter the first occurrence. When found, the accumulator needs to be reversed and also to have the new value and the tail of the original list appended. Both of this can be done in one operation: List.fold being fed the new value and tail of the original list as initial state while the elements of the accumulator are prepended in the loop, thereby restoring their order.
module List =
// Replace the first occurrence of a specific object in a list
let replace oldValue newValue source =
let rec aux acc = function
| [] -> List.rev acc
| x::xs when x = oldValue ->
(newValue::xs, acc)
||> List.fold (fun xs x -> x::xs)
| x::xs -> aux (x::acc) xs
aux [] source
let queueTime customers n =
(List.init n (fun _ -> 0), customers)
||> List.fold (fun xs customer ->
let x = List.min xs
List.replace x (x + customer) xs )
|> List.max
queueTime [5;3;4] 1 // val it : int = 12
queueTime [10;2;3;3] 2 // val it : int = 10
queueTime [2;3;10] 2 // val it : int = 12

Why does Series.resampleUniform return an empty series?

I cannot understand why resmplU in the code below is an empty series.
I suspect the problem is I do not understand the role of the parameter nextKey in the signature of resampleUniform. The help says it "is used to generate all keys in the range," but I could not figure out what that means.
open Deedle
let dateRange skip (startDate: System.DateTime) endDate =
Seq.initInfinite float
|> Seq.map (fun i -> startDate.AddDays (skip * i))
|> Seq.takeWhile (fun dt -> dt <= endDate)
let dt3 = DateTime(2017,4,1)
let dt4 = DateTime(2017,4,30)
let dt5 = DateTime(2017,4,15)
let dateR = dateRange 2.0 dt3 dt4
let valsR = [1..dateR |> Seq.length]
let tseries = Series(dateR, valsR)
let resmplU =
tseries
|> Series.resampleUniform Lookup.ExactOrGreater (fun x -> x < dt5) id
After running this code I get:
val dt3 : DateTime = 4/1/2017 12:00:00 AM
val dt4 : DateTime = 4/30/2017 12:00:00 AM
val dt5 : DateTime = 4/15/2017 12:00:00 AM
val dateR : seq<DateTime>
val valsR : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15]
val tseries : Series<DateTime,int> =
series [ 4/1/2017 12:00:00 AM => 1; 4/3/2017 12:00:00 AM => 2; 4/5/2017 12:00:00 AM => 3; 4/7/2017 12:00:00 AM => 4; 4/9/2017 12:00:00 AM => 5; ... ; 4/29/2017 12:00:00 AM => 15]
val resmplU : Series<bool,Series<DateTime,int>> = series [ ]
Any insights?
The Series.resampleUniform function lets you rescale values in a series to a new set of keys. For example, given your series:
01/04/2017 -> 1
03/04/2017 -> 2
05/04/2017 -> 3
07/04/2017 -> 4
09/04/2017 -> 5
11/04/2017 -> 6
13/04/2017 -> 7
15/04/2017 -> 8
17/04/2017 -> 9
19/04/2017 -> 10
21/04/2017 -> 11
23/04/2017 -> 12
25/04/2017 -> 13
27/04/2017 -> 14
29/04/2017 -> 15
You can resample the series so that the keys are days in the month from 1 to 29. For each key (day), you will get values on that specific day, or values on the next day for which they are available:
tseries
|> Series.resampleUniform Lookup.ExactOrGreater (fun dt -> dt.Day) (fun d -> d + 1)
The first function dt -> dt.Day turns keys from the original series into keys in the new series (to illustrate how this works, I'm using int as keys for the returned series) and the second function d -> d + 1 calculates the next key of the target series.
EDIT I think the problem with your idea of using bool as the key is that the resampling function needs to calculate one more key after the two keys in the series you want to get - so that it can make sure it reached the end. This does not work for bool as there are only two values. The following works though:
tseries
|> Series.resampleUniform Lookup.ExactOrGreater
(fun x -> if x < dt5 then 0 else 1) (fun n -> n + 1)

Is there a way to go from Gen<a> to Gen<a list> while specifying a bound?

module BirthdayLibrary =
type Birthday =
{ day :int
month :int }
module DataGen =
let birthdayGenerator (months:option<list<int>>) =
let monthGen = match months with
| Some m -> Gen.elements m
| None -> FsCheck.Gen.choose(1,12)
(fun m d -> { day = d
month = m}:BirthdayLibrary.Birthday)
<!> monthGen
<*> FsCheck.Gen.choose(1,28)
//I want this to have the signature Gen<BirthdayLibrary.Birthday list>
let oneForEveryMonthGen =
[ for m in [1 .. 12] ->
(fun mo -> birthdayGenerator (Some [mo]))]
Lets say I have something like the above. I have a birthday generator with the return type of Gen<BirthdayLibrary.Birthday>. You can optionally specify the month. What is the best way to go from a Gen<a> to a Gen<a list> WHILE specifying some constraint to item in the list's gen? I can think of ways to get <Gen<a> list>, which I don't think would work for composing a parent object that should contain a <list a> (which is the basis for this example). I can also think of a way to do Gen<Gen<a> list>, which I had composition problems with as well as all values in the list being the same. I saw Gen.arrayOf bit I can't think of how to get it to work with values for each item in the array.
module BirthdayLibrary =
type Birthday =
{ day :int
month :int }
module DataGen =
let birthdayGenerator (months:option<list<int>>) =
let monthGen = match months with
| Some m -> Gen.elements m
| None -> FsCheck.Gen.choose(1,12)
(fun m d -> { day = d
month = m}:BirthdayLibrary.Birthday)
<!> monthGen
<*> FsCheck.Gen.choose(1,28)
//I want this to have the signature Gen<BirthdayLibrary.Birthday list>
let oneForEveryMonthGen =
[ for m in [1 .. 12] -> birthdayGenerator (Some [m])]
|> Gen.sequence
let sample = DataGen.oneForEveryMonthGen |> Gen.sample 10 1
gives me:
val sample : BirthdayLibrary.Birthday list list = [[{day = 7;
month = 1;}; {day = 15;
month = 2;}; {day = 13;
month = 3;}; {day = 14;
month = 4;}; {day = 28;
month = 5;};
{day = 9;
month = 6;}; {day = 28;
month = 7;}; {day = 15;
month = 8;}; {day = 6;
month = 9;}; {day = 21;
month = 10;};
{day = 12;
month = 11;}; {day = 27;
month = 12;}]]

Using F# match to extract two days out of the week

Learning to use F#, and I'm trying to get familiar with the match expression. I expect the below code to pick two consecutive days out of the week, the current day and the day after. It only picks out the current day. What am I missing here?
DayOfWeek array:
let days = [|DayOfWeek.Sunday, true;
DayOfWeek.Monday, false;
DayOfWeek.Tuesday, true;
DayOfWeek.Wednesday, true;
DayOfWeek.Thursday, true;
DayOfWeek.Friday, true;
DayOfWeek.Saturday, true;|]
Match expression:
let curDate = DateTime.Now
let validDates =
[
for i in days do
match i with
| day, true ->
match day with
| x when int x = int curDate.DayOfWeek ||
int x > int curDate.DayOfWeek
&& int x - int curDate.DayOfWeek = 1 ->
yield
x
| _ -> ()
|_ -> ()
]
Your solution seems extremely convoluted to me, and like others have mentioned it only works if the underlying int value of tomorrow's DayOfWeek is one greater than today's. As you know, the week is a cycle so that logic won't always hold true. I don't want to spoonfeed, but there is a much easier solution:
let today = DateTime.Now.DayOfWeek
let days = [|DayOfWeek.Sunday, true;
DayOfWeek.Monday, false;
DayOfWeek.Tuesday, true;
DayOfWeek.Wednesday, true;
DayOfWeek.Thursday, true;
DayOfWeek.Friday, true;
DayOfWeek.Saturday, true;|]
let today_and_tomorrow =
let idx_today = Array.findIndex (fun (day, _) -> day = today) days
days.[idx_today], days.[idx_today + 1 % days.Length]
For me, the difficulty is with using pattern matching.
Here is how I would do it without, allowing you to take any number of days, not just two.
open System
let next count days day =
seq { while true do yield! days } // make the days array infinite
|> Seq.skipWhile (fun (d, _) -> d <> day) // skip until we find our day
|> Seq.filter (fun (_, incl) -> incl) // get rid of 'false' days
|> Seq.take count // take the next 'count' of days
|> Seq.map (fun (d, _) -> d) // we only care about the day now, so a simple map gets rid of the boolean
Using your array of days, I get the following:
DayOfWeek.Sunday
|> next 2 days
val it : seq<DayOfWeek> = seq [Sunday; Tuesday]
and
DayOfWeek.Thursday
|> next 3 days
val it : seq<DayOfWeek> = seq [Thursday; Friday; Saturday]
and
DayOfWeek.Sunday
|> next 10000 days
|> Seq.iter (printfn "%A")
Well, I'm not going to print what this one does, you'll just have to use your imagination. :)
I hope that helps!
Edit I made it handle an infinite number of days.
I think you can write this a lot easier by using the Enum-caps of F#/.net:
open System;;
let weekdayAfter (day : DateTime) : DayOfWeek =
int day.DayOfWeek
|> (fun i -> (i+1) % 7)
|> Microsoft.FSharp.Core.LanguagePrimitives.EnumOfValue<_, _>
let today_and_tomorrow =
let today = DateTime.Today
(today.DayOfWeek, weekdayAfter today)
And if you really want to use pattern-matching then why not go with the readable/obvious solution:
let dayAfter (day : DateTime) =
match day.DayOfWeek with
| DayOfWeek.Sunday -> DayOfWeek.Monday
| DayOfWeek.Monday -> DayOfWeek.Tuesday
| DayOfWeek.Tuesday -> DayOfWeek.Wednesday
| DayOfWeek.Wednesday -> DayOfWeek.Thursday
| DayOfWeek.Thursday -> DayOfWeek.Friday
| DayOfWeek.Friday -> DayOfWeek.Saturday
| DayOfWeek.Saturday -> DayOfWeek.Sunday
| _ -> failwith "should never happen"

Merge multiple arrays in f#

I have three sets of information that I need to join together into one array so I can calculate a payment.
Dataset 1:
FromDate, ToDate
2013-04-10, 2013-04-16
(i'm currently creating a 2D array of the dates between these two dates using the following)
let CalculatedLOS : int = ToDate.Value.Subtract(FromDate.Value).Days
let internalArray = Array2D.init CalculatedDays, 3, (fun x -> (AdmissionDateValue.AddDays(x),0,0))
Dataset 2: These are separated as: code, date | code, date
87789,2013-04-10|35444,2013-04-14
Dataset 3: These are separated as date, differentcode | date, differentcode
2013-04-10,SE|2013-04-15,EA
What I need to do is somehow match up the dates with the relevant index in the array that is created from the FromDate and ToDate and update the 2nd and 3rd position with the code and differentcode that match to that date.
So I would hopefully end up with a dataset that looked like this
[2013-04-10; 87789; SE][2013-04-11;;][2013-04-12;;][2013-04-13;;][2013-04-14;87789;][2013-04-15;;EA][2013-04-16;;]
I would then iterate over this array to lookup some values and assign a payment based on each day.
I've tried Array.find within a loop to update 2D arrays but I'm not sure how to do it (code below which did not work) but I'm really stuck about how to do this, or even if this is the best way.
let differentCodeArray = MyLongString.Value.Split('|')
for i in 0 .. bedStaysArray.Length - 1 do
Array.find(fun elem -> bedStaysArray.[0].ToString() elem) internalArray
Also happy to be directed away from arrays if there's a better way!
Here is one way of doing it, given i understand your question. The code have a dependency on the 'correct' DateFormat beeing used.
Full example, dataset1, dataset2, dataset3 are your given inputs.
//Given data
let dataset1 = "2013-04-10, 2013-04-16"
let dataset2 = "87789,2013-04-10|35444,2013-04-14"
let dataset3 = "2013-04-10,SE|2013-04-15,EA"
//Extract data
let keyValuePair (c:char) (str:string) = let [|a;b|] = str.Split(c) in a,b
let mapTuple fn a = fn (fst a), fn (snd a)
let date1,date2 = keyValuePair ',' dataset1 |> mapTuple System.DateTime.Parse
let data2 =
dataset2.Split('|')
|> Seq.map (keyValuePair ',')
|> Seq.map (fun (code, date) -> System.DateTime.Parse date, code)
|> Map.ofSeq
let data3 =
dataset3.Split('|')
|> Seq.map (keyValuePair ',')
|> Seq.map (fun (date, code) -> System.DateTime.Parse date, code)
|> Map.ofSeq
let rec dateSeq (a:System.DateTime) (b:System.DateTime) =
seq {
yield a.Date
if a < b then yield! dateSeq (a.AddDays(1.0)) b
}
//join data
let getCode data key = match data |> Map.tryFind key with |Some v -> v |None -> ""
let result =
dateSeq date1 date2
|> Seq.map (fun d -> d, getCode data2 d, getCode data3 d)
|> Seq.toList
//Format result
result |> List.iter ((fun (date, code1, code2) -> printfn "[%s;%s;%s]" (date.ToShortDateString()) code1 code2))
Console output:
[2013-04-10;87789;SE]
[2013-04-11;;]
[2013-04-12;;]
[2013-04-13;;]
[2013-04-14;35444;]
[2013-04-15;;EA]
[2013-04-16;;]

Resources