72

I am trying to match the beginning of strings in f#. Not sure if I have to treat them as a list of characters or what. Any suggestions would be appreciated.

Here is a psuedo code version of what I am trying to do

let text = "The brown fox.."

match text with
| "The"::_ -> true
| "If"::_ -> true
| _ -> false

So, I want to look at the beginning of the string and match. Note I am not matching on a list of strings just wrote the above as an idea of the essence of what I am trying to do.

Jeff
  • 1,181
  • 1
  • 10
  • 17

3 Answers3

110

Parameterized active patterns to the rescue!

let (|Prefix|_|) (p:string) (s:string) =
    if s.StartsWith(p) then
        Some(s.Substring(p.Length))
    else
        None

match "Hello world" with
| Prefix "The" rest -> printfn "Started with 'The', rest is %s" rest
| Prefix "Hello" rest -> printfn "Started with 'Hello', rest is %s" rest
| _ -> printfn "neither"
Brian
  • 117,631
  • 17
  • 236
  • 300
  • 1
    +1. A nice example of active patterns for us wanting to learn as well! – Muhammad Alkarouri Sep 15 '10 at 23:45
  • 1
    This answer has a lovely application of compositions active patterns to string matching: http://stackoverflow.com/questions/3686199/f-pattern-composition/3686555#3686555 – Dmitry Lomov Sep 16 '10 at 00:52
  • 1
    This answer is fantastic, but I must admit I'm flummoxed. Where does the "rest" value come from in the match expression? – Bryan Slatner Nov 29 '15 at 20:44
  • 1
    Take a look at the active patterns link in the answer, section partial active patterns. The rest comes from the underline (_) in the function definition. – vgaltes Dec 14 '15 at 20:44
23

You could also use a guard on the pattern:

match text with
| txt when txt.StartsWith("The") -> true
| txt when txt.StartsWith("If") -> true
| _ -> false
trgt
  • 411
  • 4
  • 3
16

Yes you have to treat them as a list of characters if you want to use a match expression.

Simply transform the string with:

let text = "The brown fox.." |> Seq.toList

Then you can use a match expression but you will have to use chars (the type of elements in the list) for each letter:

match text with
| 'T'::'h'::'e'::_ -> true
| 'I'::'f'::_ -> true
| _ -> false

As Brian suggest Parameterized Active Patterns are much nicer, there a some useful patterns here (go the end of the page).

elmattic
  • 12,046
  • 5
  • 43
  • 79
  • While I prefer Brian's answer using active patterns, I wonder if this one might be more efficient if the possible prefixes are only a few and not long. Any insights on this? – Alexander Rautenberg Sep 16 '10 at 09:40
  • You have to create a temporary array and a F# list, so possibly lots of objects that would add pressure on the GC. This is not optimal at all. You'd better use two `StartsWith` or `StartsWith` + AP. – elmattic Sep 16 '10 at 12:13
  • 4
    Since strings are already a `char seq`, you could just use `Seq.toList`. – YotaXP Sep 16 '10 at 16:41