Why does the second pattern matching return "1 and 2" and give me a warning "This rule will never be matched" on the wildcard _?
let a = [3;4]
match a with
|[1;2] -> "1 and 2"
|_ -> "Other"
|> printfn "%A"
let lst = [1;2]
match a with
|lst -> "1 and 2"
|_ -> "Other"
|> printfn "%A"
It appears to me lst is considered truthy. How so?
An identifier in lowercase matches with everything and binds the identifier to the value.
If you want to compare lst with a you need to use when which is a conditional guard:
let a = [3;4]
match a with
|[1;2] -> "1 and 2"
|_ -> "Other"
|> printfn "%A"
let lst = [1;2]
match a with
| b when b = lst -> "1 and 2"
|_ -> "Other"
|> printfn "%A"
but in this case a simple if then else would work.
In your 2nd pattern matching you have two different lst variables with the same name. Here,
let lst = [1;2]
match a with
| lst -> "1 and 2"
| _ -> "Other"
in the 1st match case you don't refer to the lst variable above, you create a new one. You can check it by trying to refactor-rename it (F2 in Visual Studio). As AMieres explains, case is important.
In general, referring to a variable in a match case is doable yet not trivial.
Consider this code:
let helloWorld = "hello world"
let isHelloWorld s =
match s with
| helloWorld -> true
| _ -> false
Here you will get the same warning as described. One way to go is to mark the constant with the [<Literal>] attribute and to make it upper-case at the same time:
[<Literal>]
let HelloWorld = "hello world"
let isHelloWorld s =
match s with
| HelloWorld -> true
| _ -> false
This would work as expected. However, you can apply the [<Literal>] attribute only to certain types, and list is not among them. For those, you have to leverage when conditions in match cases.
Related
Sometimes I use something like this:
match foo a with
| 1 -> printfn "%s" (foo a)
| 0 -> printfn "ok"
In this case I call foo function twice and if it is expensive call I use this code:
let tmp = foo a
match tmp with
| 1 -> printfn "%s" tmp
| 0 -> printfn "ok"
But in this case I have created variable with outer scope (regarding match expression).
I am looking for something like this:
match (foo a) as tmp with
| 1 -> printfn "%s" tmp
| 0 -> printfn "ok
What do you use in this cases ? Is there any elegant solution ?
Update - real example:
let collection = getValuesFromDatabase a
match Array.length collection with
| 0 -> printfn "nothing"
| _ -> bar collection.[0]
Option 1: use a let- or do-block
let result =
let tmp = foo a
match tmp with
| 1 -> printfn "%d" tmp
| 0 -> printfn "ok"
Nesting the whole thing under a let-block keeps from polluting the namespace with tmp. The syntax is a bit heavy, but in return it allows for arbitrary complexity of the local computation.
Alternatively, if your result is a unit, you can replace let with do:
do
let tmp = foo a
match tmp with
| 1 -> printfn "%d" tmp
| 0 -> printfn "ok"
Option 2: use pattern aliasing
When pattern-matching, you can match a value with more than one pattern at once, separating the patterns with &, e.g.:
match [1;2;3] with
| (x::_)&(_::y::_) -> printfn "First element is %d, second element is %d" x y
Here, I am matching the same list with two patterns: x::_ and _::y::_. The example is a bit silly (I could have just matched with x::y::_), but it conveys the idea.
In your example, you can use this mechanism to capture the whole value by matching it with a trivial pattern:
match foo a with
| 1&x -> printfn "%d" x
| 0 -> printfn "ok"
Update: the "real" example
This is in response to your edit, where you provided a "real" example, which deals with a collection.
This "real" example is actually different from the "toy" examples that you provided before, in that you want to capture collection, but you're matching on Array.length collection - not the same thing. In general, there is no shortcut for this, except putting it in a nested do or let block as described above. But in your specific case I could rewrite the match like this:
match getValuesFromDatabase a with
| [||] -> printfn "nothing"
| xs -> bar xs.[0]
Here, instead of calling Array.length, I match the value with an empty array. This way, since I'm matching the collection itself, I can capture it in the second match case and use it to get the first element.
If you wanted to perform a more complex check than just the empty array check, you could also use a pattern guard:
match getValuesFromDatabase a with
| xs when Array.length xs = 0 -> printfn "nothing"
| xs -> bar xs.[0]
In your real example, you can just use if. You are not really pattern matching on any complex data type which is where match shines. If you're testing whether a collection is empty, you can just write something like:
let collection = getValuesFromDatabase a
if Array.length collection = 0 then printfn "nothing"
else bar collection.[0]
I'm learning F# and I've started to play around with both sequences and match expressions.
I'm writing a web scraper that's looking through HTML similar to the following and taking the last URL in a parent <span> with the paging class.
<html>
<body>
<span class="paging">
Link to Google
The Link I want
</span>
</body>
</html>
My attempt to get the last URL is as follows:
type AnHtmlPage = FSharp.Data.HtmlProvider<"http://somesite.com">
let findMaxPageNumber (page:AnHtmlPage)=
page.Html.Descendants()
|> Seq.filter(fun n -> n.HasClass("paging"))
|> Seq.collect(fun n -> n.Descendants() |> Seq.filter(fun m -> m.HasName("a")))
|> Seq.last
|> fun n -> n.AttributeValue("href")
However I'm running into issues when the class I'm searching for is absent from the page. In particular I get ArgumentExceptions with the message: Additional information: The input sequence was empty.
My first thought was to build another function that matched empty sequences and returned an empty string when the paging class wasn't found on a page.
let findUrlOrReturnEmptyString (span:seq<HtmlNode>) =
match span with
| Seq.empty -> String.Empty // <----- This is invalid
| span -> span
|> Seq.collect(fun (n:HtmlNode) -> n.Descendants() |> Seq.filter(fun m -> m.HasName("a")))
|> Seq.last
|> fun n -> n.AttributeValue("href")
let findMaxPageNumber (page:AnHtmlPage)=
page.Html.Descendants()
|> Seq.filter(fun n -> n.HasClass("paging"))
|> findUrlOrReturnEmptyStrin
My issue is now that Seq.Empty is not a literal and cannot be used in a pattern. Most examples with pattern matching specify empty lists [] in their patterns so I'm wondering: How can I use a similar approach and match empty sequences?
The suggestion that ildjarn gave in the comments is a good one: if you feel that using match would create more readable code, then make an active pattern to check for empty seqs:
let (|EmptySeq|_|) a = if Seq.isEmpty a then Some () else None
let s0 = Seq.empty<int>
match s0 with
| EmptySeq -> "empty"
| _ -> "not empty"
Run that in F# interactive, and the result will be "empty".
You can use a when guard to further qualify the case:
match span with
| sequence when Seq.isEmpty sequence -> String.Empty
| span -> span
|> Seq.collect (fun (n: HtmlNode) ->
n.Descendants()
|> Seq.filter (fun m -> m.HasName("a")))
|> Seq.last
|> fun n -> n.AttributeValue("href")
ildjarn is correct in that in this case, an if...then...else may be the more readable alternative, though.
Use a guard clause
match myseq with
| s when Seq.isEmpty s -> "empty"
| _ -> "not empty"
Building on the answer from #rmunn, you can make a more general sequence equality active pattern.
let (|Seq|_|) test input =
if Seq.compareWith Operators.compare input test = 0
then Some ()
else None
match [] with
| Seq [] -> "empty"
| _ -> "not empty"
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
I'm trying to create DU cases from strings. The only way I can see doing this is by enumerating over the DU cases via Microsoft.FSharp.Reflection.FSharpType.GetUnionCases and then picking the UnionCase that matches the string (by using .Name) and then making the actual DU case out of that by using FSharpValue.MakeUnion.
Isn't there an easier/more elegant way of doing this? In my scenario I have a DU with a couple of hundred cases for keywords. I have to read the strings (keywords) from a file and make the types out of them. I did some "optimization" by putting the cases into a Map but I was hoping there'd be a better way of doing this.
I have the following, for example:
type Keyword =
| FOO
| BAR
| BAZ
| BLAH
let mkKeywords (file: string) =
use sr = new StreamReader(file)
let caseMap =
FSharpType.GetUnionCases(typeof<Keyword>)
|> Array.map (fun c -> (c.Name, FSharpValue.MakeUnion(c, [||]) :?> Keyword))
|> Map.ofArray
[
while not sr.EndOfStream do
let l = sr.ReadLine().Trim()
match caseMap.TryFind l with
| Some c -> yield c
| None -> failwith <| "Could not find keyword: " + l
]
I found this handy code snippet...
open Microsoft.FSharp.Reflection
let toString (x:'a) =
let (case, _ ) = FSharpValue.GetUnionFields(x, typeof<'a>)
case.Name
let fromString<'a> (s:string) =
match FSharpType.GetUnionCases typeof<'a> |> Array.filter (fun case -> case.Name = s) with
|[|case|] -> Some(FSharpValue.MakeUnion(case,[||]) :?> 'a)
|_ -> None
... which makes it easy to tack on two lines of code to any DU...
type A = X|Y|Z with
override this.ToString() = FSharpUtils.toString this
static member fromString s = FSharpUtils.fromString<A> s
I would use pattern matching like this:
type Keyword =
| FOO
| BAR
| BAZ
| BLAH
let matchKeyword (word:string) : Keyword option =
match word with
| "FOO" -> Some FOO
| "BAR" -> Some BAR
| "BAZ" -> Some BAZ
| "BLAH" -> Some BLAH
| _ -> None
And maybe auto generate the match statement first time using regex in my editor, but only because you have hundreds of cases. But i am not sure if its a better solution then yours.
As the cases have no value, another option is to use enums:
type Keyword =
| FOO = 0
| BAR = 1
| BAZ = 2
| BLAH = 3
let strings = ["FOO";"BAR"]
let keywords =
[for s in strings -> s, Keyword.Parse(typeof<Keyword>, s)]
|> Map.ofList
Then you can simply use Enum.Parse.
After reading Chris' answer to F# - public literal and the blog post at http://blogs.msdn.com/b/chrsmith/archive/2008/10/03/f-zen-the-literal-attribute.aspx I don't get why the following is not working:
[<Literal>]
let one = 1
[<Literal>]
let two = 2
let trymatch x =
match x with
| one -> printfn "%A" one
| two -> printfn "%A" two
| _ -> printfn "none"
trymatch 3
This keeps printing "3", although I think it shouldn't. What is it that I don't see here?
I think that literals need to be Uppercase. The following works fine:
[<Literal>]
let One = 1
[<Literal>]
let Two = 2
let trymatch x =
match x with
| One -> printfn "%A" One
| Two -> printfn "%A" Two
| _ -> printfn "none"
trymatch 3
In addition, if you want a nice general solution for this without using literals, you can define a parameterized active pattern like this:
let (|Equals|_|) expected actual =
if actual = expected then Some() else None
And then just write
let one = 1
let two = 2
let trymatch x =
match x with
| Equals one -> printfn "%A" one
| Equals two -> printfn "%A" two
| _ -> printfn "none"
The other answers are right - you must start your identifier with an uppercase letter. See section 7.1.2 of the spec (Named Patterns), which states that:
If long-ident is a single identifier that does not begin with an uppercase character then it is always interpreted as a variable-binding pattern and represents a variable that is bound by the pattern
Also if you don't want to have Uppercase literals you can put them in a module (here named Const):
module Const =
[<Literal>]
let one = 1
[<Literal>]
let two = 2
let trymatch x =
match x with
| Const.one -> printfn "%A" Const.one
| Const.two -> printfn "%A" Const.two
| _ -> printfn "none"
trymatch 3
Don't ask me why, but it works when you write your literals uppercase:
[<Literal>]
let One = 1
[<Literal>]
let Two = 2
let trymatch (x:int) =
match x with
| One -> printfn "%A" One
| Two -> printfn "%A" Two
| _ -> printfn "none"
trymatch 3