How to use match Map elements in F#? - f#

I tried to create a function that takes two integers a,b as input and return 5 if a=1 b=2 and 6 otherwise, Here is what I did:
let examplef (a:int), (b:int)=
match a,b with
|1,2 -> 5
|_,_->6;;
It gives this error: "The pattern discriminator 'examplef' is not defined."
I ask this question because of the error in this code:
type Team = string
type Goals = Goals of int
type Points = Points of int
type Fixture = Team * Team
type Result = (Team * Goals) * (Team * Goals)
type Table = Map<Team,Points>
let league =["Chelsea"; "Spurs"; "Liverpool"; "ManCity"; "ManUnited"; "Arsenal"; "Everton"; "Leicester"]
let pointsMade (a: Result)=
match a with
|((b,Goals bg),(c,Goals cg))-> if b<c then ((b,Points 0),(c, Points 3))
elif b=c then ((b,Points 1),(c,Points 1))
else ((b, Points 3),(c, Points 0))
I get an error when trying to define the following function:
let updateTable (t:Table, r: Result)=
let pointmade = pointsMade r
match pointmade with
|((f,Points s),(f1,Points s1))-> match Map.tryFind f t Map.tryFind f1 t with
|None, None -> t
|Some Points x, Some Points y ->t .Add (f, Points s+x1) .Add(f1, Points s1+y1)
When I hover the mouse over the first "Map.tryFind f t" It says "This value is not a function and cannot be applied. Also, there is an error with t .Add (f, Points s+x1) .Add(f1, Points s1+y1) it says: "Successive arguments should be separated by space and tuples and arguments involving functions or method applications should be parenthesized".
Please help

It looks like you're confusing tuple and curried arguments.
Examples with a single tuple argument (parenthesis are requiered).
signature: int * int -> int
//let example1 (a: int, b:int) =
let example1 (a, b) =
match a, b with
| 1, 2 -> 5
| _ -> 6
//let example2 (t: int * int) =
let example2 t =
match t with
| 1, 2 -> 5
| _ -> 6
Example with two curried arguments:
signature: int-> int -> int
//let example3 (a: int) (b: int) =
let example3 a b =
match a, b with
| 1, 2 -> 5
| _ -> 6

Anyway, The code that work looks like this:
open System.Security.Cryptography
open System.Threading
type Team = string
type Goals = Goals of int
type Points = Points of int
type Fixture = Team * Team
type Result = (Team * Goals) * (Team * Goals)
type Table = Map<Team,Points>
let league =["Chelsea"; "Spurs"; "Liverpool"; "ManCity"; "ManUnited"; "Arsenal"; "Everton"; "Leicester"]
let pointsMade (a: Result)=
match a with
|((b,Goals bg),(c,Goals cg))-> if bg<cg then ((b,Points 0),(c, Points 3))
elif bg=cg then ((b,Points 1),(c,Points 1))
else ((b, Points 3),(c, Points 0))
let initEntry (name:Team)=(name, Points 0)
let initializeTable l = Map.ofList (List.map initEntry l)
let updateTable (t:Table, r: Result)=
let pointmade = pointsMade r
match pointmade with
|((f,Points s),(f1,Points s1))-> match Map.tryFind f t, Map.tryFind f1 t with
|None, None -> t
|Some x, Some y-> match x,y with
| Points x1 , Points y1 -> t |> Map.add f (Points(x1+s)) |> Map.add f1 (Points (y1+s1))
|None, Some y -> match y with
| Points y1 -> t.Add(f,Points s) .Add(f1, Points (s1+y1))
|Some x, None -> match x with
| Points x1 -> t.Add(f,Points (s+x1)) .Add(f1, Points s1)
let rec weekendUpdate (t:Table , rl:Result list)=
match rl with
|[]->t
|ai::at-> weekendUpdate(updateTable(t,ai),at)
let rec seasonUpdate (t:Table, sll: Result list list)=
match sll with
|[]->t
|ah::at-> seasonUpdate(weekendUpdate(t,ah),at)
let less((s1,n1):Team * Points, (s2,n2):Team * Points) =
match n1,n2 with
|Points m1,Points m2 ->if m1<m2 then true
else false
let rec myinsert item lst =
match lst with
| [] -> [item]
| x::xs -> if less(item,x) then x::(myinsert item xs) else item::lst
let rec isort lst =
match lst with
| [] -> []
| x::xs -> myinsert x (isort xs)
let showStandings (t:Table) = isort (Map.toList t)

In your "working code", in the pointsMade function you do not need to use pattern matching, you can simply use a let binding.
let pointsMade (r: Result) =
let (t1, Goals g1), (t2, Goals g2) = r
if g1 < g2 then (t1, Points 0), (t2, Points 3)
elif g1 = g2 then (t1, Points 1), (t2, Points 1)
else (t1, Points 3), (t2, Points 0)
The updateTable function also can be re-written in more concise way by using some addPoints function to avoid repeating the same thing for each team.
let addPoints (team: Team, Points points) (table: Table) =
match Map.tryFind team table with
| None -> table
| Some (Points p) -> Map.add team (Points (points + p)) table
let updateTable (table: Table, result: Result) =
let pts1, pts2 = pointsMade result
table |> addPoints pts1 |> addPoints pts2

Related

Using List filter to impose a boolean condition on a list and returning a new filtered list

Given a source and target grid point, write the function: dist: p1: pos -> p2: pos -> int
I have successfully created this function as shown below:
type pos = int*int
let p1 = (1, 1) // source grid point
let p2 = (3, 3) // target grid point
let dist (p1: pos) (p2: pos) : int =
(((pown ((fst p2)-(fst p1)) 2) + (pown ((snd p2)-(snd p1)) 2)))
dist p1 p2
printfn "%A" (dist p1 p2)
Given a source and a target and dist, write the function
candidates: src: pos -> tg: pos -> pos list
which returns the list of candidate next positions, which brings the robot closer to its
target. I.e., if src = (x, y), then the function must consider all the neighbouring positions,
{(x+1, y),(x−1, y),(x, y+1),(x, y−1)}, and return those whose distance is equal to or
smaller than dist(src,tg). This can be done with List.filter.
This is what I have so far:
let src = p1
let tg = p2
let candidates (src: pos) (tg: pos) : pos list =
let candi = [((fst p1)+1), (snd p1); ((fst p1)-1), (snd p1); (fst p1), ((snd p1)+1); (fst p1), ((snd p1)-1)]
let candilist = candi |> List.filter (fun x -> x <=dist p1 p2)
candilist
printfn "%A" (candidates p1 p2)
Since my dist function returns an int, I get an error message saying: This expression was expected to have type
'int * int'
but here has type
'int'
I hope someone can give a few tips.
This line:
let candilist = candi |> List.filter (fun x -> x <=dist p1 p2)
should probably read:
let candilist = candi |> List.filter (fun x -> dist x p2 <= dist p1 p2)
The filter function should return true for points which are closer to the target than the current point. At present you are comparing the candidate point itself (of type int * int) with the distance (of type int) from the target.

Possible to optimize pattern-matching code segment in one line?

I am wondering if there is a way to write this line without piping h to calcVol function twice?
| h :: t when (h |> calcVol) > maxVol -> maxLoop t (h |> calcVol)
Where h is a tuple containing three dimensions, and calcVol returns a float value.
I know that I could explicitly define a vol value as:
| h :: t ->
let vol = calcVol h
if vol > maxVol then...
I am wondering if there is a way to do this nicely in one line?
If all the uses of vol were before the arrow, you could do this:
| h :: t when let vol = (h |> calcVol) in vol > maxVol -> // Something
But let assignments in the when clause left of the arrow do not carry over to the right-hand side. Demonstration:
let f x = x + 5
let l = [1; 2]
match l with
| a :: b when let y = f a in y = 6 -> "Six"
| _ -> "Other"
This works, and returns "Six". But:
let f x = x + 5
let l = [1; 2]
match l with
| a :: b when let y = f a in y = 6 -> sprintf "Six = %d" y
| _ -> "Other"
This does not work, producing the error:
error FS0039: The value or constructor 'y' is not defined.
So unfortunately, you can't have the one-line version you want and you'll have to go with the longer approach (with a let followed by an if, as you demonstrate in the second half of your answer).
Using active patterns a solution could look like this:
let calcVol v = v
let (|MaxVol|) maxVol = function
| [] -> (maxVol, [])
| h :: t -> ((max (calcVol h) maxVol), t)
let rec maxLoop list m =
match list with
| [] -> m
| MaxVol m (c, t) -> maxLoop t c
let vs = [ -1; 42; 3 ]
maxLoop vs System.Int32.MinValue // 42
Another possibility with better readability might be to first calculate the volumes (e.g. by mapping) and then find the maximum. Difficult to tell without the complete code...

Subtract two Maps of Map<'a, int>

I have the following type:
type Multiset<'a when 'a: comparison> = MSet of Map<'a, int>
I want to declare a function for this type that subtracts two MSets.
Let's say I have the following two Multisets:
let f = MSet (Map.ofList [("a",1);("b",2);("c",1)])
let g = MSet (Map.ofList [("a",1);("b",3);("c",1)])
I have now tried to create this subtract function which takes two Multisets.
let subtract fms sms =
match fms with
| MSet fs -> match sms with
| MSet ss ->
let toList ms = Map.fold (fun keys key value -> keys # [for i = 1 to value do yield key] ) [] ms
let fromList l = match l with
| [] -> MSet(Map.ofList [])
| x::xs -> MSet(Map.ofList (x::xs |> Seq.countBy id |> Seq.toList))
let sfList = toList fs
let ssList = toList ss
fromList (List.filter (fun n -> not (List.contains n sfList)) ssList)
If I run :
subtract f g
It returns :
MSet (map [])
Which is not what I wanted. g contains one more b than f, so I would want it to return:
MSet(map [("b", 1)])
My implementation doesn't account for multiple occurrences of the same key. I am not quite sure how I can fix this, so I get the wanted functionality?
I suspect you just have your arguments reversed, that's all. Try subtract g f.
That said, your solution seems way more complicated than it needs to be. How about just updating the values in the first map by subtracting the counts in the second, then removing non-positive counts?
let sub (MSet a) (MSet b) =
let bCount key = match Map.tryFind key b with | Some c -> c | None -> 0
let positiveCounts, _ =
a
|> Map.map (fun key value -> value - (bCount key))
|> Map.partition (fun _ value -> value > 0)
MSet positiveCounts
Also, the nested match in your implementation doesn't need to be there. If you wanted to match on both arguments, you can just do:
match fms, sms with
| MSet fs, MSet ss -> ...
But even that is an overkill - you can just include the pattern in parameter declarations, like in my implementation above.
As for duplicate keys - in this case, there is no reason to worry: neither of the arguments can have duplicate keys (because they're both Maps), and the algorithm will never produce any.
The underlying issue, also evident in your other question, seems to be the unification of identical keys. This requires an equality constraint and can be easily effected by the high-level function Seq.groupBy. Since comparison isn't strictly necessary, I propose using a dictionary, but the approach would work also with maps.
Given a type
type MultiSet<'T> = MultiSet of System.Collections.Generic.IDictionary<'T, int>
and a helper which maps the keys, sums their values and validates the result;
let internal mapSum f =
Seq.groupBy (fun (KeyValue(k, _)) -> f k)
>> Seq.map (fun (k, kvs) -> k, Seq.sumBy (fun (KeyValue(_, v)) -> v) kvs)
>> Seq.filter (fun (_, v) -> v > 0)
>> dict
>> MultiSet
your operations become:
let map f (MultiSet s) =
mapSum f s
let add (MultiSet fms) (MultiSet sms) =
Seq.append fms sms
|> mapSum id
let subtract (MultiSet fms) (MultiSet sms) =
Seq.map (fun (KeyValue(k, v)) ->
System.Collections.Generic.KeyValuePair(k, -v)) sms
|> Seq.append fms
|> mapSum id
let f = MultiSet(dict["a", 1; "b", 2; "c", 1])
let g = MultiSet(dict["a", 1; "b", 3; "c", 1])
subtract f g
// val it : MultiSet<string> = MultiSet (seq [])
subtract g f
// val it : MultiSet<string> = MultiSet (seq [[b, 1] {Key = "b";
// Value = 1;}])

F# stream of armstrong numbers

I am seeking help, mainly because I am very new to F# environment. I need to use F# stream to generate an infinite stream of Armstrong Numbers. Can any one help with this one. I have done some mambo jumbo but I have no clue where I'm going.
type 'a stream = | Cons of 'a * (unit -> 'a stream)
let rec take n (Cons(x, xsf)) =
if n = 0 then []
else x :: take (n-1) (xsf());;
//to test if two integers are equal
let test x y =
match (x,y) with
| (x,y) when x < y -> false
| (x,y) when x > y -> false
| _ -> true
//to check for armstrong number
let check n =
let mutable m = n
let mutable r = 0
let mutable s = 0
while m <> 0 do
r <- m%10
s <- s+r*r*r
m <- m/10
if (test n s) then true else false
let rec armstrong n =
Cons (n, fun () -> if check (n+1) then armstrong (n+1) else armstrong (n+2))
let pos = armstrong 0
take 5 pos
To be honest your code seems a bit like a mess.
The most basic version I could think of is this:
let isArmstrong (a,b,c) =
a*a*a + b*b*b + c*c*c = (a*100+b*10+c)
let armstrongs =
seq {
for a in [0..9] do
for b in [0..9] do
for c in [0..9] do
if isArmstrong (a,b,c) then yield (a*100+b*10+c)
}
of course assuming a armstrong number is a 3-digit number where the sum of the cubes of the digits is the number itself
this will yield you:
> Seq.toList armstrongs;;
val it : int list = [0; 1; 153; 370; 371; 407]
but it should be easy to add a wider range or remove the one-digit numbers (think about it).
general case
the problem seems so interesting that I choose to implement the general case (see here) too:
let numbers =
let rec create n =
if n = 0 then [(0,[])] else
[
for x in [0..9] do
for (_,xs) in create (n-1) do
yield (n, x::xs)
]
Seq.initInfinite create |> Seq.concat
let toNumber (ds : int list) =
ds |> List.fold (fun s d -> s*10I + bigint d) 0I
let armstrong (m : int, ds : int list) =
ds |> List.map (fun d -> bigint d ** m) |> List.sum
let leadingZero =
function
| 0::_ -> true
| _ -> false
let isArmstrong (m : int, ds : int list) =
if leadingZero ds then false else
let left = armstrong (m, ds)
let right = toNumber ds
left = right
let armstrongs =
numbers
|> Seq.filter isArmstrong
|> Seq.map (snd >> toNumber)
but the numbers get really sparse quickly and using this will soon get you out-of-memory but the
first 20 are:
> Seq.take 20 armstrongs |> Seq.map string |> Seq.toList;;
val it : string list =
["0"; "1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"; "9"; "153"; "370"; "371";
"407"; "1634"; "8208"; "9474"; "54748"; "92727"; "93084"]
remark/disclaimer
this is the most basic version - you can get big speed/performance if you just enumerate all numbers and use basic math to get and exponentiate the digits ;) ... sure you can figure it out

Develop a function that takes a list of integers that defines it the longest continuous chain of identical numbers

Please help.
Develop a function that takes a list of integers that defines it the longest continuous chain of identical numbers. The result of the function must be a pair (number, length of the chain)
my code:-
let findMaxSeq (nums: int list) =
let foldFun (curN, len, (curWinN, curWinLen)) n =
match len, curWinLen with
| 0, 0 -> (n, 1, (n,1))
| 0, _ -> (n, 1, (curWinN,curWinLen))
| _ when n = curN ->
let newLen = len+1
if (newLen>curWinLen) then (n, newLen, (n, newLen)) else (n, newLen, (curWinN, curWinLen))
| _ -> (n, 1, (curWinN, curWinLen))
let (_, _, (winner)) = nums |> List.fold foldFun (0, 0, (0, 0))
winner
but don't compile - in second let i have error:-
Block following this 'let' is unfinished. Expect an expression.
Could indentation be the problem? It compiles when formatted like this:
let findMaxSeq (nums: int list) =
let foldFun (curN, len, (curWinN, curWinLen)) n =
match len, curWinLen with
| 0, 0 -> (n, 1, (n,1))
| 0, _ -> (n, 1, (curWinN,curWinLen))
| _ when n = curN ->
let newLen = len+1
if (newLen>curWinLen) then (n, newLen, (n, newLen))
else (n, newLen, (curWinN, curWinLen))
| _ -> (n, 1, (curWinN, curWinLen))
let (_, _, (winner)) = nums |> List.fold foldFun (0, 0, (0, 0))
winner
This looked like a fun challenge, so I took a stab at it.
let findMaxRepeatedValue xs =
let rec loop (maxVal, maxCount) (curVal, curCount) = function
| [] -> if curCount > maxCount then (curVal, curCount) else (maxVal, maxCount)
| x::xs when x = curVal -> loop (maxVal, maxCount) (curVal, curCount + 1) xs
| x::xs ->
if curCount > maxCount then loop (curVal, curCount) (x, 1) xs
else loop (maxVal, maxCount) (x, 1) xs
match xs with
| [] -> invalidArg "xs" "empty list"
| [x] -> (x, 1)
| x::xs -> loop (x, 1) (x, 1) xs
Since your main question has been answered, here's yet another option/approach, for fun and profit :)
let longestChain nums =
let rec chain lst (num, cnt) = seq {
match lst with
| x :: xs -> if x = num then
yield! chain xs (num, cnt+1)
else
yield (num, cnt)
yield! chain xs (x, 1)
| [] -> yield (num, cnt)
}
match nums with
| x :: xs -> chain xs (x, 1) |> Seq.maxBy snd
| [] -> failwith "Cannot find the longest chain in an empty list"
As Daniel pointed out, this was just an indentation problem - F# is an indentation sensitive language (spaces have meaning) and so you need to indent nested blocks further. Your function works just fine when it is indented correctly!
let findMaxSeq (nums: int list) =
let foldFun (curN, len, (curWinN, curWinLen)) n =
match len, curWinLen with
| 0, 0 -> (n, 1, (n,1))
| 0, _ -> (n, 1, (curWinN,curWinLen))
| _ when n = curN ->
let newLen = len+1
if (newLen>curWinLen) then
(n, newLen, (n, newLen))
else
(n, newLen, (curWinN, curWinLen))
| _ -> (n, 1, (curWinN, curWinLen))
let (_, _, (winner)) = nums |> List.fold foldFun (0, 0, (0, 0))
winner
findMaxSeq [1;2;2;2;3;3;1;1;1;1;1;4;4]
Note that:
the body of function foldFun is indented further than the let that defines the function.
the body of the complex pattern (matching when n=curN) is also indented further
I also split if then to multiple lines (for readability - this is not required)
Daniel's solution is perfectly fine too - but since you asked about a version based on List.fold, I thought I'd answer with a corrected version of your original code.
As an aside, if you wanted to do more operations like this on some actual data (like time series) rather than just solve this for the purpose of learning F#, then Deedle which is a library for working with series data has a nice abstraction called chunkWhile that splits series into chunks while some condition holds (e.g. while the values are the same) and makes it pretty easy to write this:
#r "lib/Deedle.dll"
open Deedle
let findMaxSeq values =
let s = Series.ofValues values
s |> Series.chunkWhile (fun k1 k2 -> s.[k1] = s.[k2])
|> Series.map(fun k chunk -> s.[k], Series.countKeys chunk)
|> Series.values
|> Seq.maxBy snd
findMaxSeq [1;2;2;2;3;3;1;1;1;1;1;4;4]
Here's an attempt which is generic and uses standard library functions. Since you didn't say what the answer should be when the input sequence is empty, I'm not returning a pair of number * length directly, but wrap that up in an option.
let inline findMaxSeq xs =
xs
|> Seq.scan (fun state x ->
match state with
| Some (y, i) when x = y -> Some (x, i + 1)
| _ -> Some (x, 1) )
None
|> Seq.maxBy (function
| Some (_, i) -> i
| _ -> 0 )
findMaxSeq [1;2;2;2;3;3;1;1;1;1;1;4;4] // Some (1, 5)
findMaxSeq Seq.empty<int> // None
Sorry, but most F# code I've seen her so far looks to me like C# in disguise. I am sure functional F# programmers can do better, along the lines of this Haskell solution:
maxrv = maximumBy (comparing fst) . map (\xs -> (length xs, xs)) . group

Resources