How to construct a match expression - parsing

I am allowing a command-line parameter like this --10GB, where -- and GB are constant, but a number like 1, 10, or 100 could be substituted in between the constant values, like --5GB.
I could easily parse the start and end of the string with substr or written a command line parser, but wanted to use match instead. I am just not sure how to structure the match expression.
let GB1 = cvt_bytes_to_gb(int64(DiskFreeLevels.GB1))
let arg = argv.[0]
let match_head = "--"
let match_tail = "GB"
let parse_min_gb_arg arg =
match arg with
| match_head & match_tail -> cvt_gb_arg_to_int arg
| _ -> volLib.GB1
I get a warning saying _ This rule will never be matched. How should the what is an AND expression be constructed?

You can't match on strings, except matching on the whole value, e.g. match s with | "1" -> 1 | "2" -> 2 ...
Parsing beginning and end would be the most efficient way to do this, there is no need to get clever (this, by the way, is a universally true statement).
But if you really want to use pattern matching, it is definitely possible to do, but you'll have to make yourself some custom matchers (also known as "active patterns").
First, make a custom matcher that would parse out the "middle" part of the string surrounded by prefix and suffix:
let (|StrBetween|_|) starts ends (str: string) =
if str.StartsWith starts && str.EndsWith ends then
Some (str.Substring(starts.Length, str.Length - ends.Length - starts.Length))
else
None
Usage:
let x = match "abcd" with
| StrBetween "a" "d" s -> s
| _ -> "nope"
// x = "bc"
Then make a custom matcher that would parse out an integer:
let (|Int|_|) (s: string) =
match System.Int32.TryParse s with
| true, i -> Some i
| _ -> None
Usage:
let x = match "15" with
| Int i -> i
| _ -> 0
// x = 15
Now, combine the two:
let x = match "--10GB" with
| StrBetween "--" "GB" (Int i) -> i
| _ -> volLib.GB1
// x = 10
This ability of patterns to combine and nest is their primary power: you get to build a complicated pattern out of small, easily understandable pieces, and have the compiler match it to the input. That's basically why it's called "pattern matching". :-)

The best I can come up with is using a partial active pattern:
let (|GbFormat|_|) (x:string) =
let prefix = "--"
let suffix = "GB"
if x.StartsWith(prefix) && x.EndsWith(suffix) then
let len = x.Length - prefix.Length - suffix.Length
Some(x.Substring(prefix.Length, len))
else
None
let parse_min_gb_arg arg =
match arg with
| GbFormat gb -> gb
| _ -> volLib.GB1
parse_min_gb_arg "--42GB"

Related

How can I use TryParse in a guard expression in match?

I have built a toy spreadsheet to help learn F#. When I process the text for a new cell I store it as a discriminated type. To parse it I feel I should be able to do something like:
let cv =
match t with
| _ when t.Length=0 -> Empty
| x when t.[0]='=' -> Expr(x)
| x when t.[0]='\"' -> Str(x)
| (true,i) when Int32.TryParse t -> IntValue(i) // nope!
| _ -> Str(t)
I have tried quite a few combinations but I cannot get TryParse in the guard. I have written a helper:
let isInt (s:string) =
let mutable m:Int64 = 0L
let (b,m) = Int64.TryParse s
b
I can now write:
| _ when Utils.isInt t -> IntValue((int)t)
This seems like a poor solution as it discards the converted result. What the correct syntax to get TryParse into the guard?
I think an active pattern will do what you want:
let (|Integer|_|) (str: string) =
let flag, i = Int32.TryParse(str)
if flag then Some i
else None
let cv =
match t with
| _ when t.Length=0 -> Empty
| x when t.[0]='=' -> Expr(x)
| x when t.[0]='\"' -> Str(x)
| Integer i -> IntValue(i)
| _ -> Str(t)
But if you really want TryParse in the guard condition (and you don't mind parsing twice), you could do this:
| x when fst (Int32.TryParse(t)) -> IntValue (Int32.Parse(x))

Pattern matching numeric strings

I have a function that pattern matches its argument, which is a string:
let processLexime lexime
match lexime with
| "abc" -> ...
| "bar" -> ...
| "cat" -> ...
| _ -> ...
This works as expected. However, I'm now trying to extend this by expressing "match a string containing only the following characters". In my specific example, I want anything containing only digits to be matched.
My question is, how can I express this in F#? I'd prefer to do this without any libraries such as FParsec, since I'm mainly doing this for learning purposes.
You can use active patterns: https://msdn.microsoft.com/en-us/library/dd233248.aspx
let (|Integer|_|) (str: string) =
let mutable intvalue = 0
if System.Int32.TryParse(str, &intvalue) then Some(intvalue)
else None
let parseNumeric str =
match str with
| Integer i -> printfn "%d : Integer" i
| _ -> printfn "%s : Not matched." str
One way would be an active pattern
let (|Digits|_|) (s:string) =
s.ToCharArray() |> Array.forall (fun c -> System.Char.IsDigit(c)) |> function |true -> Some(s) |false -> None
then you can do
match "1" with
|Digits(t) -> printf "matched"
I would use regular expressions combined with active patterns. With regular expressions you can easily match digits with \d and active patterns makes the syntax nice inside your match.
open System.Text.RegularExpressions
let (|ParseRegex|_|) regex str =
let m = Regex("^"+regex+"$").Match(str)
if (m.Success) then Some true else None
let Printmatch s =
match s with
| ParseRegex "w+" d -> printfn "only w"
| ParseRegex "(w+|s+)+" d -> printfn "only w and s"
| ParseRegex "\d+" d -> printfn "only digis"
|_ -> printfn "wrong"
[<EntryPoint>]
let main argv =
Printmatch "www"
Printmatch "ssswwswwws"
Printmatch "134554"
Printmatch "1dwd3ddwwd"
0
which prints
only w
only w and s
only digis
wrong

Converting strings in F#

I am attempting to convert a file name like the following:
ten_of_clubs.png
to
10_of_clubs.png
However, I do not like my implementation:
let getFile (card: Card Option) =
let fileName =
match card with
| Some card -> (getUnionTypeName card.Face + "_of_" + getUnionTypeName card.Suit + ".png").ToLower()
| None -> ""
let delimitedName = fileName.Split '_'
let currentFace = delimitedName.[0]
let updatedFace =
match currentFace with
| "two" -> "2"
| "three" -> "3"
| "four" -> "4"
| "five" -> "5"
| "six" -> "6"
| "seven" -> "7"
| "eight" -> "8"
| "nine" -> "9"
| "ten" -> "10"
| _ -> currentFace
updatedFace + "_" + delimitedName.[1] + "_" + delimitedName.[2]
In F#, how can I just change the first element of a string without referencing all elements?
Example:
updatedFace + "_" + delimitedName.[1] + "_" + delimitedName.[2]
to
updatedFace + "_" + delimitedName.[1..]
There's no reason to first construct a formatted string, then split it, and finally create a new formatted string.
Assuming that your intent is to return the empty string in the None case, you can do something like this:
let getFile card =
let digitize candidate =
match candidate with
| "Two" -> "2"
| "Three" -> "3"
| "Four" -> "4"
| "Five" -> "5"
| "Six" -> "6"
| "Seven" -> "7"
| "Eight" -> "8"
| "Nine" -> "9"
| "Ten" -> "10"
| _ -> candidate
match card with
| Some c ->
let face = c.Face |> string |> digitize
let suit = c.Suit |> string
sprintf "%s_of_%s.png" face suit
| None -> ""
Here, I've assumed that the Face and Suit types override ToString instead of relying on a function called getUnionTypeName that I don't know of. This enables you to use the built-in function string (which simply calls ToString ()).
Ad hoc tests:
> Some { Face = Ten; Suit = Clubs } |> getFile;;
val it : string = "10_of_Clubs.png"
> Some { Face = Jack; Suit = Clubs } |> getFile;;
val it : string = "Jack_of_Clubs.png"
> None |> getFile;;
val it : string = ""
That digitize function looks like a candidate for a general-purpose function, and I wonder if the BCL doesn't already have something like that lying around in its Globalization namespace... Otherwise, I'm sure there are hundreds of NuGet packages out there that implement such functionality...
It turns out that there aren't hundreds of NuGet packages that already do this, so I created one, called Numsense. With it, you can simplify the getFile function to this:
open Ploeh.Numsense
let getFile card =
let digitize candidate =
match Numeral.tryParseEnglish candidate with
| Some i -> string i
| None -> candidate
match card with
| Some c ->
let face = c.Face |> string |> digitize
let suit = c.Suit |> string
sprintf "%s_of_%s.png" face suit
| None -> ""
After splitting your string by _ you get an array of parts. Array is mutable Collection. You can change First by your function and then concatenate by _ again.
let parts = myString.Split [|'_'|]
parts.[0] <- toNumber parts.[0]
String.concat "_" parts

F# Develop a function counting the number of branches containing only "leaves"

I have expression - (cos(9**5)-cos(8*5))*(sin(3+1)**exp(6*6)).
I present this expression in type -
type common =
Exp of common*common
| Sin of common
| Cos of common
| Bin of common*string*common
| Digit of float
| Exponent of common
let expr = Bin(Bin(Cos(Exp(Digit(9.0),Digit(5.0))),"-",Cos(Bin(Digit(8.0),"*",Digit(5.0)))),"*",Exp(Sin(Bin(Digit(3.0),"+",Digit(1.0))),Exponent(Bin(Digit(6.0),"*",Digit(6.0)))));
I have function that calculate expression -
let rec evalf com =
match com with
Digit(x) -> x
|Exp(d1,d2) -> let dig1 = evalf(d1)
let dig2 = evalf(d2)
System.Math.Pow(dig1,dig2)
|Sin(d) -> let dig = evalf(d)
System.Math.Sin(dig)
|Cos(d) -> let dig = evalf(d)
System.Math.Cos(dig)
|Exponent(d) -> let dig = evalf(d)
System.Math.Exp(dig)
|Bin(d1,op,d2) -> let dig1 = evalf(d1)
let dig2 = evalf(d2)
match op with
| "*" -> dig1*dig2
| "+" -> dig1+dig2
| "-" -> dig1-dig2
I need develop a function counting the number of branches containing only "leaves". Please help.
If you defines "leaves" as digits, then to count the number of branches containing only "leaves" you would need to count the number of expressions that only reference digits.
This can be achieved with a recursive function similar to evalf, that returns 1 for branches with only "leaves"/digits and recurses for the non-digit cases e.g.
let rec count expr =
match expr with
| Expr(Digit(_),Digit(_) -> 1
| Expr(d1,d2) -> count d1 + count d2
| Sin(Digit(_)) -> 1
| Sin(d) -> count d
// ... for all cases
A similar technique can be used to simplify an expression tree, for example a binary operation (Bin) on 2 numbers could be matched and simplified to a single number. This might be used for example as a compiler optimization step.

F# matching with two values

I'm fairly new to F# and I wanted to compare two values with the (match ... with ...) syntax
The problem arises when I attempt to compare two values like this:
let value1 = 19
let isValue1 y =
match y with
| value1 -> y + 1
| _ -> y
I get a warning that the "| _ -> y" portion of the code will never be reached. Why is this?
I know that I can do the following to get the function to work the way I want it to:
let value1 = 19
let isValue1 y =
match y with
| _ when y = value1 -> true
| _ -> false
This works as well
let value1 = 19
let isValue1 y =
match y with
| 19 -> true
| _ -> false
I'm just curious about why I can't do that, and how match actually works.
The value1 within the match statement is defined as a new variable, the value of which is set to y (as a match). The value1 you define just above is ignored, just as if you were declaring a local variable in a C# function with the same name as a class variable. For this reason, the first match condition will match everything, not just the previously defined value of value1, hence the error. Hope that clarifies matters.
Pattern-matching is both a control construct (what code executes next) and a binding construct (like 'let', bind a name to a value). So when you do
match expr with
| name -> ...
the pattern ("name") always matches and the identifier 'name' just gets bound to the value of the expression. This is why pattern-matching is mostly used with discriminated unions (case types), where you match based on the structure. E.g.
match someOption with
| Some(x) -> ... // binds x
| None -> ...
match someList with
| h :: t -> ... // binds h and t to head/tail
| [] -> ...
You can just match the Input to Literals/Identifiers marked by the [<Literal>] Attribute without binding it.
For Example:
#light
[<Literal>]
let E = 2.718281828459
let isE x =
match x with
| E -> true
| _ -> false
print_any (isE 3.2)
print_any (isE E)
According to Crish Smith

Resources