F# less than operator in pattern matching - f#

For some reason the less than operator in this pattern match will not work. It's the only error I have and it's driving me insane.
I'm probably missing something really obvious but all help is appreciated.
let CheckAccount account =
match account with
| {Balance < 10.00} -> Console.WriteLine("Balance is Low")
| {Balance >= 10.00 and <= 100.00} -> Console.WriteLine("Balance is OK")
| {Balance > 100.00} -> Console.WriteLine("Balance is High")
This is the type:
type Account = {AccountNumber:string
mutable Balance:float}
member this.Withdraw(amnt:float) =
if amnt > this.Balance then
Console.WriteLine("Unable to withdraw. The Amount you wish to withdraw is greater than your current balance.")
else
this.Balance <- this.Balance - amnt
Console.WriteLine("You have withdrawn £" + amnt.ToString() + ". Your balance is now: £" + this.Balance.ToString())
member this.Deposit(amnt:float) =
this.Balance <- this.Balance + amnt
Console.WriteLine("£" + amnt.ToString() + " Deposited. Your new Balance is: £" + this.Balance.ToString())
member this.Print =
Console.WriteLine("Account Number: " + this.AccountNumber)
Console.WriteLine("Balance: £" + this.Balance.ToString())

You can use pattern matching to extract the balance value, bind it to a new name and then compare the values using the when clause:
let CheckAccount account =
match account with
| {Balance = b} when b < 10.00 -> Console.WriteLine("Balance is Low")
| {Balance = b} when b >= 10.00 && b <= 100.00 -> Console.WriteLine("Balance is OK")
| {Balance = b} when b > 100.00 -> Console.WriteLine("Balance is High")
I would say that in this case, you are not actually getting much from using pattern matching. If you wrote the same code using if, then it would probably look nicer.
You can use a bit fancier approach and define active patterns that let you compare values:
let (|LessThan|_|) k value = if value < k then Some() else None
let (|MoreThan|_|) k value = if value > k then Some() else None
Then you can use those instead:
let CheckAccount account =
match account with
| {Balance = LessThan 10.0} -> Console.WriteLine("Balance is Low")
| {Balance = LessThan 100.0 & MoreThan 10.0 } -> Console.WriteLine("Balance is OK")
This is actually fairly interesting - because you can use the & construct to combine multiple active patterns as in LessThan 100.0 & MoreThan 10.0.

Related

F#: Not understanding match .. with

I'm messing around with F# and Fable, and trying to test my understanding. To do so, I tried creating a function to calculate e given a certain number of iterations. What I've come up with is
let eCalc n =
let rec internalECalc ifact sum count =
match count = n with
| true -> sum
| _ -> internalECalc (ifact / (float count)) (sum + ifact) (count+1)
internalECalc 1.0 0.0 1
Which works fine, returning 2.7182818284590455 when called with
eCalc 20
However, if I try using, what I think is, the more correct form
let eCalc n =
let rec internalECalc ifact sum count =
match count with
| n -> sum
| _ -> internalECalc (ifact / (float count)) (sum + ifact) (count+1)
internalECalc 1.0 0.0 1
I get a warning "[WARNING] This rule will never be matched (L5,10-L5,11)", and returned value of 0. (and the same thing happens if I swap 'n' and 'count' in the match statement). Is there a reason I can't use 'n' in the match statement? Is there a way around this so I can use 'n'?
Thanks
When you use a name in a match statement, you're not checking it against the value assigned to that variable the way you think you are. You are instead assigning that name. I.e.,
match someInt with
| n -> printfn "%d" n
will print the value of someInt. It's the equivalent of let n = someInt; printfn "%d" n.
What you wanted to do was use a when clause; inside a when clause, you're not pattern-matching, but doing a "standard" if check. So what you wanted was:
let eCalc n =
let rec internalECalc ifact sum count =
match count with
| cnt when cnt = n -> sum
| _ -> internalECalc (ifact / (float count)) (sum + ifact) (count+1)
internalECalc 1.0 0.0 1
Does that make sense, or do you need me to go into more detail?
P.S. In a case like this one where your match function looks like "x when (boolean condition involving x) -> case 1 | _ -> case 2", it's quite a bit more readable to use a simple if expression:
let eCalc n =
let rec internalECalc ifact sum count =
if count = n then
sum
else
internalECalc (ifact / (float count)) (sum + ifact) (count+1)
internalECalc 1.0 0.0 1

How to handle negative power in F#?

I'm trying to build the nth power function in F#. (Yes, there's already Math.Pow in .Net). Here is my attempt:
let rec nthPower x n =
match n with
| 0 -> 1
| _ -> x * (nthPower x (n-1))
This works fine when n >= 0; however, I don't know how to handle the negative case: when n < 0.
Question:
How to handle the negative case? (n<0)
Is this recursive algorithm efficient? or are there any efficient ways in F#?
You can implement it like this:
let rec nthPower x n =
match n with
| 0 -> 1m
| t when t < 0 -> 1m / (nthPower x -n)
| _ -> decimal x * (nthPower x (n - 1));;
The t when t < 0 allows the pattern matching to match a range of values. I would say that the RHS of this line is self-explanatory, but let me know if it's unclear.
Regarding question #2, I don't think there's anything particularly inefficient about this approach and there's probably not a much simpler way to do it. I'm not sure what the most efficient approach is, but hopefully some mathematicians can chime in.
Edit: I have found an approach that is more efficient for exponents > ~10. It uses memoization and divide-and-conquer to compute the result in O(log n) time instead of O(n):
let rec nthPower x n =
match n with
| 0 -> 1.0
| 1 -> double x
| t when t < 0 -> 1.0 / (nthPower x -n)
| _ ->
let p = nthPower x (n / 2)
p * p * nthPower x (n % 2)

Aggregation function - f# vs c# performance

I have a function that I use a lot and hence the performance needs to be as good as possible. It takes data from excel and then sums, averages or counts over parts of the data based on whether the data is within a certain period and whether it is a peak hour (Mo-Fr 8-20).
The data is usually around 30,000 rows and 2 columns (hourly date, value). One important feature of the data is that the date column is chronologically ordered
I have three implementations, c# with extension methods (dead slow and I m not going to show it unless somebody is interested).
Then I have this f# implementation:
let ispeak dts =
let newdts = DateTime.FromOADate dts
match newdts.DayOfWeek, newdts.Hour with
| DayOfWeek.Saturday, _ | DayOfWeek.Sunday, _ -> false
| _, h when h >= 8 && h < 20 -> true
| _ -> false
let internal isbetween a std edd =
match a with
| r when r >= std && r < edd+1. -> true
| _ -> false
[<ExcelFunction(Name="aggrF")>]
let aggrF (data:float[]) (data2:float[]) std edd pob sac =
let newd =
[0 .. (Array.length data) - 1]
|> List.map (fun i -> (data.[i], data2.[i]))
|> Seq.filter (fun (date, _) ->
let dateInRange = isbetween date std edd
match pob with
| "Peak" -> ispeak date && dateInRange
| "Offpeak" -> not(ispeak date) && dateInRange
| _ -> dateInRange)
match sac with
| 0 -> newd |> Seq.averageBy (fun (_, value) -> value)
| 2 -> newd |> Seq.sumBy (fun (_, value) -> 1.0)
| _ -> newd |> Seq.sumBy (fun (_, value) -> value)
I see two issues with this:
I need to prepare the data because both date and value are double[]
I do not utilize the knowledge that dates are chronological hence I do unnecessary iterations.
Here comes now what I would call a brute force imperative c# version:
public static bool ispeak(double dats)
{
var dts = System.DateTime.FromOADate(dats);
if (dts.DayOfWeek != DayOfWeek.Sunday & dts.DayOfWeek != DayOfWeek.Saturday & dts.Hour > 7 & dts.Hour < 20)
return true;
else
return false;
}
[ExcelFunction(Description = "Aggregates HFC/EG into average or sum over period, start date inclusive, end date exclusive")]
public static double aggrI(double[] dts, double[] vals, double std, double edd, string pob, double sumavg)
{
double accsum = 0;
int acccounter = 0;
int indicator = 0;
bool peakbool = pob.Equals("Peak", StringComparison.OrdinalIgnoreCase);
bool offpeakbool = pob.Equals("Offpeak", StringComparison.OrdinalIgnoreCase);
bool basebool = pob.Equals("Base", StringComparison.OrdinalIgnoreCase);
for (int i = 0; i < vals.Length; ++i)
{
if (dts[i] >= std && dts[i] < edd + 1)
{
indicator = 1;
if (peakbool && ispeak(dts[i]))
{
accsum += vals[i];
++acccounter;
}
else if (offpeakbool && (!ispeak(dts[i])))
{
accsum += vals[i];
++acccounter;
}
else if (basebool)
{
accsum += vals[i];
++acccounter;
}
}
else if (indicator == 1)
{
break;
}
}
if (sumavg == 0)
{
return accsum / acccounter;
}
else if (sumavg == 2)
{
return acccounter;
}
else
{
return accsum;
}
}
This is much faster (I m guessing mainly because of the exit of loop when period ended) but oviously less succinct.
My questions:
Is there a way to stop iterations in the f# Seq module for sorted series?
Is there another way to speed up the f# version?
can somebody think of an even better way of doing this?
Thanks a lot!
Update: Speed comparison
I set up a test array with hourly dates from 1/1/13-31/12/15 (roughly 30,000 rows) and corresponding values. I made 150 calls spread out over the date array and repeated this 100 times - 15000 function calls:
My csharp implementation above (with string.compare outside of loop)
1.36 secs
Matthews recursion fsharp
1.55 secs
Tomas array fsharp
1m40secs
My original fsharp
2m20secs
Obviously this is always subjective to my machine but gives an idea and people asked for it...
I also think one should keep in mind this doesnt mean recursion or for loops are always faster than array.map etc, just in this case it does a lot of unnecessary iterations as it doesnt have the early exit from iterations that the c# and the f# recursion method have
Using Array instead of List and Seq makes this about 3-4 times faster. You do not need to generate a list of indices and then map over that to lookup items in the two arrays - instead you can use Array.zip to combine the two arrays into a single one and then use Array.filter.
In general, if you want performance, then using array as your data structure will make sense (unless you have a long pipeline of things). Functions like Array.zip and Array.map can calculate the entire array size, allocate it and then do efficient imperative operation (while still looking functional from the outside).
let aggrF (data:float[]) (data2:float[]) std edd pob sac =
let newd =
Array.zip data data2
|> Array.filter (fun (date, _) ->
let dateInRange = isbetween date std edd
match pob with
| "Peak" -> ispeak date && dateInRange
| "Offpeak" -> not(ispeak date) && dateInRange
| _ -> dateInRange)
match sac with
| 0 -> newd |> Array.averageBy (fun (_, value) -> value)
| 2 -> newd |> Array.sumBy (fun (_, value) -> 1.0)
| _ -> newd |> Array.sumBy (fun (_, value) -> value)
I also changed isbetween - it can be simplified into just an expression and you can mark it inline, but that does not add that much:
let inline isbetween r std edd = r >= std && r < edd+1.
Just for completeness, I tested this with the following code (using F# Interactive):
#time
let d1 = Array.init 1000000 float
let d2 = Array.init 1000000 float
aggrF d1 d2 0.0 1000000.0 "Test" 0
The original version was about ~600ms and the new version using arrays takes between 160ms and 200ms. The version by Matthew takes about ~520ms.
Aside, I spent the last two months at BlueMountain Capital working on a time series/data frame library for F# that would make this a lot simpler. It is work in progress and also the name of the library will change, but you can find it in BlueMountain GitHub. The code would look something like this (it uses the fact that the time series is ordered and uses slicing to get the relevant part before filtering):
let ts = Series(times, values)
ts.[std .. edd] |> Series.filter (fun k _ -> not (ispeak k)) |> Series.mean
Currently, this will not be as fast as direct array operations, but I'll look into that :-).
An immediate way to speed it up would be to combine these:
[0 .. (Array.length data) - 1]
|> List.map (fun i -> (data.[i], data2.[i]))
|> Seq.filter (fun (date, _) ->
into a single list comprehension, and also as the other matthew said, do a single string comparison:
let aggrF (data:float[]) (data2:float[]) std edd pob sac =
let isValidTime = match pob with
| "Peak" -> (fun x -> ispeak x)
| "Offpeak" -> (fun x -> not(ispeak x))
| _ -> (fun _ -> true)
let data = [ for i in 0 .. (Array.length data) - 1 do
let (date, value) = (data.[i], data2.[i])
if isbetween date std edd && isValidTime date then
yield (date, value)
else
() ]
match sac with
| 0 -> data |> Seq.averageBy (fun (_, value) -> value)
| 2 -> data.Length
| _ -> data |> Seq.sumBy (fun (_, value) -> value)
Or use a tail recursive function:
let aggrF (data:float[]) (data2:float[]) std edd pob sac =
let isValidTime = match pob with
| "Peak" -> (fun x -> ispeak x)
| "Offpeak" -> (fun x -> not(ispeak x))
| _ -> (fun _ -> true)
let endDate = edd + 1.0
let rec aggr i sum count =
if i >= (Array.length data) || data.[i] >= endDate then
match sac with
| 0 -> sum / float(count)
| 2 -> float(count)
| _ -> float(sum)
else if data.[i] >= std && isValidTime data.[i] then
aggr (i + 1) (sum + data2.[i]) (count + 1)
else
aggr (i + 1) sum count
aggr 0 0.0 0

OR pattern matching

I'm trying to use an OR pattern, as described here:
let foo = function
| Some (0, x) when x > 0 | None -> "bar"
| _ -> "baz"
However, this gives a compiler error:
error FS0010: Unexpected symbol '|' in pattern matching. Expected '->'
or other token.
What am I doing wrong? Does it have to do with the when guard?
A when guard refers to a single case, regardless of how many patterns are combined. The cases need to be separated:
let foo = function
| Some (0, x) when x > 0 -> "bar"
| None -> "bar"
| _ -> "baz"
For that reason, it may be better to factor out the return value, so a possibly complex expression isn't repeated:
let foo value =
let ret = "bar"
match value with
| Some (0, x) when x > 0 -> ret
| None -> ret
| _ -> "baz"
Using an active pattern is another way to avoid such repetition:
let (|Bar|_|) = function
| Some(0, x) when x > 0 -> Some()
| None -> Some()
| _ -> None
let foo = function
| Bar -> "bar"
| _ -> "baz"
You'll need two separate match cases there because the two cases bind different sets of variables (x and nothing, respectively):
| Some(0, x) when x>0 -> "bar"
| None -> "bar"
A nice trick I sometime use when you want to guard only specific bindings of a label, in a very complex pattern, is to use my own active patterns and the & (and) pattern operator:
let (|GreaterThan|_|) lowerLimit n =
if n > lowerLimit then Some () else None
let (|LesserThan|_|) upperLimit n =
if n < upperLimit then Some () else None
let (|GreaterOETo|_|) lowerLimit n =
if n >= lowerLimit then Some () else None
let (|LesserOETo|_|) upperLimit n =
if n <= upperLimit then Some () else None
let (|InRange|_|) (lowerLimit, upperLimit) n =
if n >= lowerLimit && n <= upperLimit then Some () else None
let (|Even|Odd|) n =
if n % 2 = 0 then
Even (n / 2)
else
Odd (n / 2)
type Union =
| A of int
| B of int
| A' of int
let getSpecialCases = function
| A (Even (x & GreaterThan 4 & LesserOETo 16))
| A (Odd (x & GreaterThan 0))
| B (x & LesserOETo 0)
| A' (Even (x & InRange (5, 16)))
| A' (Odd (x & GreaterThan 0)) -> Some x
| _ -> None
And of course you can just make a function to active pattern wrapper:
let (|P|_|) pred x =
if pred x then Some () else None
let ``match`` = function
| Even (x & pred (fun x -> x >= 7 && x <= 54)) -> Some x
| _ -> None

Using FSCheck generators

I have a function to generate doubles in a range:
let gen_doublein =
fun mx mn -> Arb.generate<float> |> Gen.suchThat ( (>) mx ) |> Gen.suchThat ( (<) mn )
and then a function to generate an array of 2 of these:
let gen_params:Gen<double array> =
gen { let! x = gen_doublein 0.0 20000.0
let! y = gen_doublein 0.0 2000.0
return [| x;y|] }
I put:
static member arb_params = Arb.fromGen gen_params
in the Generator class and register it. All seems OK. To test that this is all OK I have:
let f2 (xs:double array) :double= exp (-2.0*xs.[0]) + xs.[1]*exp (-2.0*xs.[0])
let fcheck fn xs = fn xs > 0.0
then using an array generator 'arrayOfLength':
Check.Quick (Prop.forAll (arrayOfLength 2) (fcheck f2))
works as expected, however:
Check.Quick (Prop.forAll (Generators.arb_params) (fcheck f2))
just starts doing some calculation and never comes back. f# gurus please help.
I did not try this, but I think the problem is that the generator creates float values randomly and then checks whether they match the predicate you specified (the range). This means that it has to generate a large number of floats before it (randomly) generates one that matches.
It would be easier to generate values in a specified range by generating float values in a range [0 .. 1]
and then re-scaling them to match the range you need.
I'm not familiar with FsCheck enough, so I don't know if there is a generator for [0 .. 1] floating-point range, but you could start by generating integers and transforming them to floats:
let gen_doublein mx mn = gen {
let! n = Arb.generate<int>
let f = float n / float Int32.MaxValue
return mx + (f * (mn - mx)) }
EDIT I see that you solved the problem already. I think the solution I posted might still be relevant for smaller ranges (where the random generator does not produce enough matching values soon enough).
The problem was the parameters were the wrong way round. Tomas's suggestion is a good one, and there are some helper functions to implement it.
// Another id function
let fd (d:double) = d
// Check that it is in bounds
let mn=1.0
let mx=5.0
let fdcheck d = (fd d <= mx) && (fd d >= mn)
// Run the check with the numbers generated within the bounds
Check.Quick (Prop.forAll (Arb.fromGen (Gen.map (fun x->
match x with
| _ when Double.IsNaN x -> (mn+mx)/2.0
| _ when x> 1e+17 ->mx
| _ when x< -1e17 ->mn
| _ -> mn + (mx-mn)*(sin x+1.0)/2.0
) Arb.generate<double>
)
) fdcheck
)
Here I have a function which passes the test if the parameter is generated correctly. I'm not sure Tomas's idea with integers works because I think that a lot of small integers are generated and so the doubles don't explore the domain much - but maybe somebody who knows FSCheck might enlighten us.
Rewritten a sample from #b1g3ar5 this way
let mapRangeNormal (min : float<_>, max : float<_>) x =
match x with
| _ when Double.IsNaN x -> (min + max) / 2.0
| _ when Double.IsPositiveInfinity x -> max
| _ when Double.IsNegativeInfinity x -> min
| _ -> min + (max - min) * (sin x + 1.0) / 2.0
let mapRangeUniform (min : float<_>, max : float<_>) x =
match x with
| _ when Double.IsNaN x -> (min + max) / 2.0
| _ when Double.IsPositiveInfinity x -> max
| _ when Double.IsNegativeInfinity x -> min
| _ when x < 0.0 ->
let newRange = max - min
min - x * (newRange / Double.MaxValue) - newRange / 2.0
| _ -> let newRange = max - min
min + x * (newRange / Double.MaxValue) + newRange / 2.0

Resources