F# pattern matching error - f#

I tried to use this code to scramble the characters into different characters and return a new list with those new characters. However, I keep getting errors saying : "a list but here has type char" on line 3, "a list list but given a char list" on the line 13 . Not sure how to fix this. Thanks in advance for the help.
let _scram x =
match x with
| [] -> [] // line 3
| 's' -> 'v'
| 'a' -> 's'
| 'e' -> 'o'
| '_' -> '_'
let rec scramble L P =
match L with
| [] -> P
| hd::t1 -> scramble t1 (P # (_scram hd))
let L =
let p = ['h'; 'e'; 'l'; 'l'; 'o'] //line 13
scramble p []

That's because you are calling the _scram as second operand of the (#) operator which concatenates two lists, so it infers that the whole expression has to be a list.
A quick fix is to enclose it into a list: (P # [_scram hd]), this way _scram hd is inferred to be an element (in this case a char).
Then you will discover your next error, the catch-all wildcard is in quotes, and even if it wouldn't, you can't use it to bind a value to be used later.
So you can change it to | c -> c.
Then your code will be like this:
let _scram x =
match x with
| 's' -> 'v'
| 'a' -> 's'
| 'e' -> 'o'
| c -> c
let rec scramble L P =
match L with
| [] -> P
| hd::t1 -> scramble t1 (P # [_scram hd])
let L =
let p = ['h'; 'e'; 'l'; 'l'; 'o']
scramble p []

F# code is defined sequentially. The first error indicates there is some problem with the code upto that point, the definition of _scram. The line | [] -> [] implies that _scram takes lists to lists. The next line | 's' -> 'v' implies that _scram takes chars to chars. That is incompatible and that explains the error.

Related

How to parse a boolean expression

I'm pretty new to F#, and I'm trying to use recursion to solve a problem.
The function receives a string, and returns a bool. The string gets parsed, and evaluated. This is bool logic, so
(T|F) returns true
(T&(T&T)) returns true
((T|T)&(T&F)) returns false
(F) = returns false
My idea was that every time I found a ), replace the part of the string from the previous ( to that ) with the result of the Comparison match. Doing this over and over until only T or F remains, to return true or false.
EDIT:
I expect it to take the string, and keep swapping out what is in between the ( and ) with the result of the comparison until it comes down to a T or F. What is happening, is an error about an incomplete structured construct. The error is in the for loop.
As I am so new to this language, I'm not sure what I'm doing wrong. Do you see it?
let ComparisonSolver (comp:string) =
let mutable trim = comp
trim <- trim.Replace("(", "")
trim <- trim.Replace(")", "")
match trim with
| "T" -> "T"
| "F" -> "F"
| "!T" -> "F"
| "!F" -> "T"
| "T&T" -> "T"
| "F&F" -> "T"
| "T&F" -> "F"
| "F&T" -> "F"
| "T|T" -> "T"
| "F|F" -> "F"
| "T|F" -> "T"
| "F|T" -> "T"
| _ -> ""
let rec BoolParser arg =
let mutable args = arg
if String.length arg = 1 then
match arg with
| "T" -> true
| "F" -> false
else
let mutable ParseStart = 0
let endRange = String.length args
for letter in [0 .. endRange]
if args.[letter] = "(" then
ParseStart <- letter
else if args.[letter] = ")" then
args <- args.Replace(args.[ParseStart .. letter], ComparisonSolver args.[ParseStart .. letter])
BoolParser args
let result = BoolParser "(T)&(F)"
There are a few things you need to correct.
for letter in [0 .. endRange] is missing a do at the end of it - it should be for letter in [0 .. endRange] do
The if comparisons in the for loop are comparing chars with strings. You need to replace "(" and ")" with '(' and ')'
for letter in [0 .. endRange] will go out of range: In F# the array construct [x..y] will go from x to y inclusive. It's a bit like in C# if you had for (int i = 0; i <= array.Length; i++). In F# you can also declare loops like this: for i = 0 to endRange - 1 do.
for letter in [0 .. endRange] will go out of range again: It's going from 0 to endrange, which is the length of args. But args is getting shortened in the for loop, so it will eventually try to get a character from args that's out of range.
Now, the problem with the if..then..else statements, which is what I think you were looking at from the beginning.
if args.[letter] = '(' then
ParseStart <- letter
else if args.[letter] = ')' then
args <- args.Replace(args.[ParseStart .. letter], ComparisonSolver args.[ParseStart .. letter])
BoolParser args
Let's take the code within the two branches as two separate functions.
The first does ParseStart <- letter, which assigns letter to ParseStart. This function returns unit, which is F# equivalent of void.
The second does:
args <- args.Replace(args.[ParseStart .. letter], ComparisonSolver args.[ParseStart .. letter])
BoolParser args
This function returns a bool.
Now when you put them together in an if..then..else statement you have in one branch that results a unit and in the other in a bool. In this case it doesn't know which one to return, so it shows an "expression was expected to have type" error.
I strongly suspect that you wanted to call BoolParser args from outside
the for/if loop. But it's been indented so that F# treats it as part of the else if statement.
There are many ways to parse a boolean expression. It might be a good idea to look at the excellent library FParsec.
http://www.quanttec.com/fparsec/
Another way to implement parsers in F# is to use Active Patterns which can make for readable code
https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/active-patterns
It's hard to provide good error reporting through Active Patterns but perhaps you can find some inpiration from the following example:
let next s i = struct (s, i) |> Some
// Skips whitespace characters
let (|SkipWhitespace|_|) struct (s, i) =
let rec loop j =
if j < String.length s && s.[j] = ' ' then
loop (j + 1)
else
next s j
loop i
// Matches a specific character: ch
let (|Char|_|) ch struct (s, i) =
if i < String.length s && s.[i] = ch then
next s (i + 1)
else
None
// Matches a specific character: ch
// and skips trailing whitespaces
let (|Token|_|) ch =
function
| Char ch (SkipWhitespace ps) -> Some ps
| _ -> None
// Parses the boolean expressions
let parse s =
let rec term =
function
| Token 'T' ps -> Some (true, ps)
| Token 'F' ps -> Some (false, ps)
| Token '(' (Parse (v, Token ')' ps)) -> Some (v, ps)
| _ -> None
and opReducer p ch reducer =
let (|P|_|) ps = p ps
let rec loop l =
function
| Token ch (P (r, ps)) -> loop (reducer l r) ps
| Token ch _ -> None
| ps -> Some (l, ps)
function
| P (l, ps) -> loop l ps
| _ -> None
and andExpression ps = opReducer term '&' (&&) ps
and orExpression ps = opReducer andExpression '|' (||) ps
and parse ps = orExpression ps
and (|Parse|_|) ps = parse ps
match (struct (s, 0)) with
| SkipWhitespace (Parse (v, _)) -> Some v
| _ -> None
module Tests =
// FsCheck allows us to get better confidence in that the parser actually works
open FsCheck
type Whitespace =
| Space
type Ws = Ws of (Whitespace [])*(Whitespace [])
type Expression =
| Term of Ws*bool
| And of Expression*Ws*Expression
| Or of Expression*Ws*Expression
override x.ToString () =
let orPrio = 1
let andPrio = 2
let sb = System.Text.StringBuilder 16
let ch c = sb.Append (c : char) |> ignore
let token (Ws (l, r)) c =
sb.Append (' ', l.Length) |> ignore
sb.Append (c : char) |> ignore
sb.Append (' ', r.Length) |> ignore
let enclose p1 p2 f =
if p1 > p2 then ch '('; f (); ch ')'
else f ()
let rec loop prio =
function
| Term (ws, v) -> token ws (if v then 'T' else 'F')
| And (l, ws, r) -> enclose prio andPrio <| fun () -> loop andPrio l; token ws '&' ;loop andPrio r
| Or (l, ws, r) -> enclose prio orPrio <| fun () -> loop orPrio l ; token ws '|' ;loop orPrio r
loop andPrio x
sb.ToString ()
member x.ToBool () =
let rec loop =
function
| Term (_, v) -> v
| And (l, _, r) -> loop l && loop r
| Or (l, _, r) -> loop l || loop r
loop x
type Properties() =
static member ``Parsing expression shall succeed`` (expr : Expression) =
let expected = expr.ToBool () |> Some
let str = expr.ToString ()
let actual = str |> parse
expected = actual
let fscheck () =
let config = { Config.Quick with MaxTest = 1000; MaxRejected = 1000 }
Check.All<Properties> config

How does function [x] -> ... work

I was browsing through the source code for FSharp.Data when I came across this line of code
let (|Singleton|) = function [l] -> l | _ -> failwith "Parameter mismatch"
The function [l] is what I don't understand. More specifically, I don't understand how the [l] parameter works.
From experimenting in the FSI, I can determine that it starts a form of pattern matching similar to match [l] with .... However, I can't figure out how the F# compiler interprets the expression.
What I'd like to know is how it actually works and what rules it follows.
It is equivalent to
let (|Singleton|) lst =
match lst with
| [l] -> l
| _ -> failwith "Parameter mismatch"
so it extracts the element from a single-element list or throws an exception if the list has any other number of elements.
See the docs on pattern matching syntax. function is a shorthand syntax for taking a single argument and immediately pattern matching:
let foo x =
match x with
| CaseA -> 1
| CaseB -> 2
Is the equivalent of
let foo = function
| CaseA -> 1
| CaseB -> 2
Note that function just adds one argument, it doesn't enforce that there is exactly one argument. For example, this is acceptable:
let foo x y = function
| CaseA -> x + y
| CaseB -> x - y
And is equivalent to
let foo x y z =
match z with
| CaseA -> x + y
| CaseB -> x - y
Edit:
(For completeness) And as for [l], like Lee said, that's just a match pattern. Specifically, a structural match pattern on lists which matches lists with a single element, and binds that element to the identifier l. See "List Pattern" here.

How can I remove a character from a string while doing character by character processing?

I'm fairly new to F# but I'm struggling to find how to properly represent the null character in the language. Can anyone tell me how to represent the null character in F#?
More to the point, what started me down the path is I'm trying to do some string processing with String.mapi, but I can't figure out how to remove a character in the below function:
let GetTargetFrameworkFolder version =
let versionMapper i c =
match c with
| 'v' -> if i = 0 then char(0x000) else c
| '.' -> char(0x000)
| _ -> c
match version with
| "v3.5" -> "net35"
| "v4.0" -> "net40"
| "v4.5" -> "net45"
| vers -> vers |> String.mapi versionMapper
GetTargetFrameworkFolder "v4.5.1" |> Dump
How can I remove a character from a string while doing character by character processing, as in the case with String.map and String.mapi?
You cannot remove a character using String.mapi, as this function maps exactly one character from the input to one character from the output. The null character is not the same thing as removing a character; it's just another character that happens to have the code 0.
In your case, if I understand correctly you want to remove the initial 'v' (if any) and remove dots. I would do it like this:
let GetTargetFrameworkFolder version =
match version with
| "v3.5" -> "net35"
| "v4.0" -> "net40"
| "v4.5" -> "net45"
| vers ->
let vers = if vers.[0] = 'v' then vers.[1..] else vers
vers.Replace(".", "")
Another way of doing this if you wanted to keep your original approach would be to write your own choose function for strings:
module String =
let choosei predicate str =
let sb = System.Text.StringBuilder()
let choose i (c:char) =
match predicate i c with
| Some(x) -> sb.Append(c) |> ignore
| None -> ()
str |> String.iteri choose
sb.ToString()
Then use it as follows:
let GetTargetFrameworkFolder version =
let versionMapper i = function
| 'v' when i = 0 -> None
| '.' -> None
| c -> Some(c)
match version with
| "v3.5" -> "net35"
| "v4.0" -> "net40"
| "v4.5" -> "net45"
| vers -> vers |> String.choosei versionMapper
GetTargetFrameworkFolder "v4.5.1" |> Dump
You can achieve this by using an array comprehension:
let GetTargetFrameworkFolder version =
match version with
| "v3.5" -> "net35"
| "v4.0" -> "net40"
| "v4.5" -> "net45"
| vers -> new String([|
for i in 0 .. vers.Length - 1 do
match i, vers.[i] with
| 0, 'v' | _, '.' -> () // skip 'v' at [0] and all '.'s
| _, c -> yield c // let everything else through
|])
By character processing while removing a character is filtering (string is a sequence of char):
let version (s: String) =
s
|> Seq.filter (fun ch -> ch <> '.' && ch <> 'v')
|> String.Concat
UPDATE:
To skip first 'v':
let version (s: String) =
s
|> Seq.skip (if s.StartsWith "v" then 1 else 0)
|> Seq.filter ((<>) '.')
|> String.Concat

i think i got infinite loop for this BST

this code i got is from Alexander Battisti about how to make a tree from a list of data:
let data = [4;3;8;7;10;1;9;6;5;0;2]
type Tree<'a> =
| Node of Tree<'a> * 'a * Tree<'a>
| Leaf
let rec insert tree element =
match element,tree with
| x,Leaf -> Node(Leaf,x,Leaf)
| x,Node(l,y,r) when x <= y -> Node((insert l x),y,r)
| x,Node(l,y,r) when x > y -> Node(l,y,(insert r x))
| _ -> Leaf
let makeTree = List.fold insert Leaf data
then i want to implement this code to my binary search tree code
let rec BinarySearch tree element =
match element,tree with
| x,Leaf -> BinarySearch (Node(Leaf,x,Leaf)) x
| x,Node(l,y,r) when x<=y ->
BinarySearch l y
| x,Node(l,y,r) when x>y ->
BinarySearch r y
| x,Node(l,y,r) when x=y ->
true
| _ -> false
then i use my search code like this:
> BinarySearch makeTree 5;;
and the result is none because it's like i got an infinite looping
can someone help me? if my code is wrong, please help me to correct it, thank you
The solution by Yin is how I would write it too.
Anyway, here is a solution that is closer to your version and (hopefully) explains what went wrong:
let rec BinarySearch tree element =
match element,tree with
| x, Leaf ->
// You originally called 'BinarySearch' here, but that's wrong - if we reach
// the leaf of the tree (on the path from root to leaf) then we know that the
// element is not in the tree so we return false
false
| x, Node(l,y,r) when x<y ->// This needs to be 'x<y', otherwise the clause would be
// matched when 'x=y' and we wouldn't find the element!
BinarySearch l element // Your recursive call was 'BinarySearch l y' but
// that's wrong - you want to search for 'element'
| x, Node(l,y,r) when x>y ->
BinarySearch r element
| x,Node(l,y,r) -> // You can simplify the code by omitting the 'when'
true // clause (because this will only be reached when
// x=y. Then you can omit the last (unreachable) case
let rec BinarySearch tree element =
match tree with
| Leaf -> false
| Node(l, v, r) ->
if v = element then
true
elif v < element then
BinarySearch r element
else
BinarySearch l element
BinarySearch makeTree 5

how to add a string to string list

let IntAndString value =
let (|Match|_|) pattern input =
let m = Regex.Match(input, pattern) in
if m.Success then Some ([ for g in m.Groups -> g.Value ]) else None
match value with
| Match "(\d+)(\w+)" x -> x
| Match "(\w+)" x -> x
| Match "(\d+)" x -> x + "MY VALUE"
| _ -> List.Empty
how can I add a string to my string list here ?
You can append to a list with the # operator. This operator concatenates two lists together, so you'll have to wrap the element you want to append in its own list like this:
| Match "(\d+)" x -> x # ["MY VALUE"]
You could also use the list constructor, ::. If prepending instead of appending is OK:
| Match "(d\+)" x -> "MY VALUE" :: x

Resources