F# pattern-matching gone wrong - f#

I am just starting out with F# so this might be a trivial question but I am not able to understand why the pattern matching in my code acts as it does.
Quick explanation of the code:
The func calcNextMatch should recurse a list and if 2 elements are equal they should be added together.
In the end the func should return a number that is the addition of all digits that has a match with the next digit in the list.
f.ex. [1;3;2;2;5] should return 4
Code:
let rec printList l =
match l with
| head :: tail -> printf "%d " head; printList tail
| [] -> printfn ""
let rec calcNextMatch list =
printList list
match list with
| [] -> 0
| _ :: tail ->
printList tail
let h = Seq.head list
let t = Seq.tryHead tail
printfn "h: %i" h
printfn "t: %O" t
match t with
| Some h ->
printfn "TAIL t: %i is equal to HEAD h: %i" t.Value h
printfn "Calculation is: %i" (t.Value + h)
(t.Value + h) + calcNextMatch tail
| _ -> calcNextMatch tail
let sequence = [ 1;3;2;2;5 ]
let run = calcNextMatch sequence
When I run this code the problem is that the pattern-matching
does not work as I expect it.
f.ex this print output from running the script.
h: 1
t: Some(3)
TAIL t: 3 is equal to HEAD h: 3
this means that F# has matched
match t with
| Some h ->
in a case where t = Some(3) and h = 1
which translates to
match 3 with
| Some 1 ->
and that I do not understand.
The print before the matching states the value of t and h to 3 and 1 but in the pattern-matching the value of h has change to 3
How is this possible?

You can only pattern match against constant literals, otherwise the value get bounded as if was a new let-binding.
In these cases what you do normally is to add a when condition:
match t with
| Some x when x = h ->
Also notice that you can use pattern match further to simplify your code, for instance here:
| _ :: tail ->
printList tail
let h = Seq.head list
You can write:
| h :: tail ->
printList tail
Also all this portion:
| _ :: tail ->
printList tail
let h = Seq.head list
let t = Seq.tryHead tail
printfn "h: %i" h
printfn "t: %O" t
match t with
| Some h ->
printfn "TAIL t: %i is equal to HEAD h: %i" t.Value h
printfn "Calculation is: %i" (t.Value + h)
(t.Value + h) + calcNextMatch tail
becomes:
| h :: tail ->
printList tail
//printfn "h: %i" h
//printfn "t: %O" t
match tail with
| t::_ when t = h ->
printfn "TAIL t: %i is equal to HEAD h: %i" t h
printfn "Calculation is: %i" (t + h)
(t + h) + calcNextMatch tail
And you can unify all matches in one, so your whole function becomes:
let rec calcNextMatch list =
printList list
match list with
| [] -> 0
| h::x::tail when x = h -> x + h + calcNextMatch (x::tail)
| _::tail -> calcNextMatch tail
Finally, when you're done with debugging, you can remove the prints and since the last parameter of your function is the one you match against, you can use the keyword function, also use an as pattern to avoid reconstructing the list:
let rec calcNextMatch = function
| [] -> 0
| h::((x::_) as tail) when x = h -> x + h + calcNextMatch tail
| _::tail -> calcNextMatch tail

Related

Match expression vs pattern matching function

I'm trying to convert the following pattern matching function to a match expression:
let reverse ls =
let rec rev acc =
function
| h :: t -> rev (h :: acc) t
| [] -> acc
rev [] ls
When I attempt to convert to equivalent match expression type mismatch errors occur:
let reverse ls =
let rec rev acc =
match acc with
| h :: t -> rev (h :: acc) t
| [] -> acc
rev [] ls
The desired output for both is:
reverse [ 1; 2; 3 ]
// val it : int list = [3; 2; 1]
Your match expression is not equivalent. To get the equivalent of your function-based code, the function should look like this:
let rec rev acc x =
match x with
| h :: t -> rev (h :: acc) t
| [] -> acc
Note the extra parameter x - that's the difference. The function keyword is equivalent not to just match, but to a function that matches on its parameter. In other words, function ... is equivalent to fun x -> match x with ...

Base case not getting picked up in my F# function

This function is supposed to just return the index of a list. That part works. However when a element is not in a list it must return -1.
For some reason it does not return -1.
let rec search f list =
match list with
| head::tail ->
if f head then 0
else 1 + search f tail
| [] -> -1
printfn "%A" (search (fun x -> x = 5) [ 5; 4; 3; 2 ])
//>> return index 0 for #5
printfn "%A" (search (fun x -> x = 6) [ 5; 4; 3; 2 ])
//>> should return -1 but it returns 3 which is the len of the list not -1
EDIT: Can not use nested functions.
You could use e.g.
let search f list =
let rec where at list =
match list with
| [] -> -1
| head::tail ->
if f head then at
else where (at + 1) tail
where 0 list
which has the benefit of being tail-recursive. Regarding your comment:
let rec search f list =
match list with
| [] -> -1
| head::tail ->
if f head then 0 else
match search f tail with
| -1 -> -1
| i -> i + 1

recursion with several functions F#

I need some help with my hometask: to express one function (sort) through others (smallest, delete, insert). If you know how, please, tell me, how I can do running my recursion cicle? it doing now only one step. maybe something like this: val4 -> head :: tail |> sort tail on line 25 (val4)?
let rec smallest = function
| x :: y :: tail when x <= y -> smallest (x :: tail)
| x :: y :: tail when x > y -> smallest (y :: tail)
| [x] -> Some x
| _ -> None
let rec delete (n, xs) =
match (n, xs) with
| (n, x :: xs) when n <> x -> x :: delete (n, xs)
| (n, x :: xs) when n = x -> xs
| (n, _) -> []
let rec insert (xs, n) =
match (xs, n) with
| ([x], n) when x < n -> [x]#[n]
| (x :: xs, n) when x < n -> x :: insert (xs, n)
| (x :: xs, n) when x >= n -> n :: x :: xs
| (_, _) -> []
let rec sort = function
| xs -> let val1 = smallest xs
let val2 = val1.[0]
let val3 = delete (val2, xs)
let val4 = insert (val3, val2)
val4
let res = sort [5; 4; 3; 2; 1; 1]
printfn "%A" res
This is sort of like insertion sort, but since you're always finding the smallest number in the whole list instead of the next highest number, it will recurse forever unless you skip whatever you've already found to be the smallest.
Furthermore, your insert and delete functions act not on the item index, but on equality to the value, so it won't be able to handle repeated numbers.
Keeping most of your original code the same, usually you have an inner recursive function to help you keep track of state. This is a common FP pattern.
let sort lst =
let size = lst |> List.length
let rec sort' xs = function
| index when index = size -> xs
| index ->
let val1 = smallest (xs |> List.skip index)
let val2 = val1.[0]
let val3 = delete (val2, xs)
let val4 = insert (val3, val2)
sort' val4 (index + 1)
sort' lst 0
let res = sort [5; 3; 2; 4; 1; ]
printfn "%A" res
Needless to say, this isn't correct or performant, and each iteration traverses the list multiple times. It probably runs in cubic time.
But keep learning!
I found it... I only had changed 4 & 5 lines above in the "smallest" on this: | [x] -> Some x
| _ -> None, when there was: | [x] -> [x]
| _ -> []
let rec sort = function
| xs -> match xs with
| head :: tail -> let val1 = smallest xs
match val1 with
| Some x -> let val2 = delete (x, xs)
let val3 = insert (val2, x)
let val4 = (fun list -> match list with head :: tail -> head :: sort tail | _ -> [])
val4 val3
| None -> []
| _ -> []
// let res = sort [5; 4; 3; 2; 1]
// printfn "%A" res

F# remove trailing space

I have this method that takes in a list and turns it into a bytecode string. It works the way I expect; however, I get one trailing space that I do not want. Question: how do I get rid of this last trailing 0?
Input: byteCode [SC 10; SC 2; SAdd; SC 32; SC 4; SC 5; SAdd; SMul; SAdd]
let rec byteCode (l : sInstr list) : string =
match l with
| [] -> ""
| (SC n :: l) -> "0 " + string n + " " + byteCode l
| (SAdd :: l) -> "1 " + byteCode l
| (SSub :: l) -> "2 " + byteCode l
| (SMul :: l) -> "3 " + byteCode l
| (SNeg :: l) -> "4 " + byteCode l
| (SLess :: l) -> "5 " + byteCode l
| (SIfze n :: l) -> "6 " + string n + " " + byteCode l
| (SJump n :: l) -> "7 " + string n + " " + byteCode l
This probably won't compile because I didn't give my entire program.
This returns: "0 10 0 2 1 0 32 0 4 0 5 1 3 1 "
I expect: "0 10 0 2 1 0 32 0 4 0 5 1 3 1"
Cases like this are usually signs that strings are concatenated in a way that is too naive. Consider first collecting all the individual components of your result and then calling the predefined String.concat function:
let byteCode (l : sInstr list) : string =
let rec byteCode' l =
match l with
| [] -> []
| (SC n :: l) -> "0" :: string n :: byteCode' l
| (SAdd :: l) -> "1" :: byteCode' l
| (SSub :: l) -> "2" :: byteCode' l
| (SMul :: l) -> "3" :: byteCode' l
| (SNeg :: l) -> "4" :: byteCode' l
| (SLess :: l) -> "5" :: byteCode' l
| (SIfze n :: l) -> "6" :: string n :: byteCode' l
| (SJump n :: l) -> "7" :: string n :: byteCode' l
l |> byteCode' |> String.concat " "
String.concat already only adds the separator string between the individual parts.
This is also much cleaner, because it keeps the implementation detail of the specific separator string out of your core logic and makes it much more easily replaceable - imagine the effort of simply changing it to two spaces in your function.
Alternatively, you can just use your existing function, and on the final resulting string call the .Trim() (or .TrimEnd()) method to remove (trailing) spaces.
You could avoid recursion in this manner:
let byteCode (l : sInstr list) : string =
let instrToString (bc : sInstr) : string =
match bc with
| (SC n) -> sprintf "0 %d" n
| (SAdd ) -> "1"
| (SSub ) -> "2"
| (SMul ) -> "3"
| (SNeg ) -> "4"
| (SLess ) -> "5"
| (SIfze n) -> sprintf "6 %d" n
| (SJump n) -> sprintf "7 %d" n
l |> List.map instrToString |> String.concat " "
Supposed sInstr is defined as:
type sInstr =
| SC of int
| SAdd
| SSub
| SMul
| SNeg
| SLess
| SIfze of int
| SJump of int
the functions to byteCodes and revserse could look like this:
let byteCode (l : sInstr list) : string =
let instrToString (bc : sInstr) =
(match bc with
| SC n -> [0; n]
| SAdd -> [1]
| SSub -> [2]
| SMul -> [3]
| SNeg -> [4]
| SLess -> [5]
| SIfze n -> [6; n]
| SJump n -> [7; n])
String.Join(" ", (l |> List.map instrToString |> List.fold (fun acc lst -> acc # lst) []))
let toInstr (bcString : string) : sInstr list =
let rec recToInstr bcList =
match bcList with
| [] -> []
| head :: tail ->
match head with
| "0" -> SC(Int32.Parse(tail.[0])) :: recToInstr (tail |> List.skip 1)
| "1" -> SAdd :: recToInstr tail
| "2" -> SSub :: recToInstr tail
| "3" -> SMul :: recToInstr tail
| "4" -> SNeg :: recToInstr tail
| "5" -> SLess :: recToInstr tail
| "6" -> SIfze(Int32.Parse(tail.[0])) :: recToInstr (tail |> List.skip 1)
| "7" -> SJump(Int32.Parse(tail.[0])) :: recToInstr (tail |> List.skip 1)
| _ -> []
recToInstr (bcString.Split(' ') |> Array.toList)

How to create a "Pair" function to match against a string list?

I was doing an exercise on F# Wiki Book on List (scroll to the bottom) to create a Pair method.
I was able to pair a integer list without problem but an F# exception was thrown for a string list. It is just too cryptic for me to decipher what the exception means for an F# beginner like me.
Here is my initial attempt to implementing Pair on fsi.exe
> let pair l =
- let rec loop acc = function
- | [] -> acc
- | (hd1 :: hd2 :: tl) -> loop ((hd1, hd2) :: acc) tl
- List.rev(loop [] l)
-
- printfn "%A" ([1..10] |> pair)
- printfn "%A" ([ "one"; "two"; "three"; "four"; "five" ] |> pair);;
let rec loop acc = function
-----------------------^
stdin(2,24): warning FS0025: Incomplete pattern matches on this expression.
For example, the value '[_]' will not be matched
val pair : 'a list -> ('a * 'a) list
[(1, 2); (3, 4); (5, 6); (7, 8); (9, 10)]
Microsoft.FSharp.Core.MatchFailureException:
Exception of type 'Microsoft.FSharp.Core.MatchFailureException' was thrown.
at FSI_0002.clo#2T.Invoke(List`1 acc, List`1 _arg1)
at FSI_0002.pair[T](List`1 l)
at <StartupCode$FSI_0002>.$FSI_0002._main()
stopped due to error
So Pair does work on integer version
and the function signature
val pair : 'a list -> ('a * 'a) list
indicates that Pair operates on a generic list.
Question: Then why would Pair not work on a string list?
[ANSWER] (my version)
Simply returning accumulated list for else case (_) did the trick.
And the warning is taken care of, as well.
let pair l =
let rec loop acc = function
// | [] -> acc
| (hd1 :: hd2 :: tl) -> loop ((hd1, hd2) :: acc) tl
| _ -> acc
List.rev(loop [] l)
printfn "%A" ([1..10] |> pair)
printfn "%A" ([ "one"; "two"; "three"; "four"; "five" ] |> pair)
[EDIT2] Well, I will also post my version of Unpair for completeness.
let unpair l = [for (a,b) in l do yield! a :: b :: []]
Here is somewhat flawed benchmarking using solution version against that of mine for 1 million item lists
#light
open System;
let pn l = printfn "%A" l
let duration f =
let startTime = DateTime.Now;
let returnValue = f()
let endTime = DateTime.Now;
printfn "Duration (ms): %f" (endTime - startTime).TotalMilliseconds
returnValue
let ll = [for a in 1..1000000 do yield (a)]
let tl = [for a in 1..1000000 do yield (a,a)]
let pair1 l =
let rec loop acc = function
| [] | [_] -> List.rev acc
| h1 :: h2 :: tl -> loop ((h1, h2) :: acc) tl
loop [] l
let unpair1 l =
let rec loop acc = function
| [] -> List.rev acc
| (h1, h2) :: tl -> loop (h2 :: h1 :: acc) tl
loop [] l
let pair2 l =
let rec loop acc = function
| (hd1 :: hd2 :: tl) -> loop ((hd1, hd2) :: acc) tl
| _ | [_] -> acc
List.rev(loop [] l)
let unpair2 l = [for (a,b) in l do yield! a :: b :: []]
pn(duration (fun() -> ll |> pair1))
pn(duration (fun() -> tl |> unpair1))
pn(duration (fun() -> ll |> pair2))
pn(duration (fun() -> tl |> unpair2))
Benchmark Result:
Solution version
PAIR -> Duration (ms): 255.000000
UNPAIR -> Duration (ms): 840.000000
My version
PAIR -> Duration (ms): 220.000000
UNPAIR -> Duration (ms): 1624.000000
I don't think your version of Pair would work on a list of an odd number of anything. You happen to test an even number of ints and a odd number of strings. I think your second argument to match implies a list of at least two members. So you break off 2 break off 2 and get to a list with 1 element and none of your conditions match.
[_] is a 1 item list with anything in it. You must provide a predicate that matches it.

Resources