http://www.markhneedham.com/blog/2009/05/10/f-regular-expressionsactive-patterns/
open System.Text.RegularExpressions let (|Match|_|) pattern input = let m = Regex.Match(input,pattern) in if m.Success then Some (List.tl [ for g in m.Groups -> g.Value ]) else None let ContainsUrl value = match value with | Match "(http:\/\/\S+)" result -> Some(result.Head) | _ -> None
如果至少找到一个网址和该网址是什么(如果我正确地理解了该片段),那会让你知道
Alternative,since a given group may
or may not be a successful match:06001
Or maybe you give labels to your
groups and you want to access them by
name:06002
试图结合所有这一切后,我想出了以下代码:
let testString = "http://www.bob.com http://www.b.com http://www.bob.com http://www.bill.com" let (|Match|_|) pattern input = let re = new Regex(pattern) let m = re.Match(input) in if m.Success then Some ((re.GetGroupNames() |> Seq.map (fun n -> (n,m.Groups.[n])) |> Seq.filter (fun (n,g) -> g.Success) |> Seq.map (fun (n,g) -> (n,g.Value)) |> Map.ofSeq)) else None let GroupMatches stringToSearch = match stringToSearch with | Match "(http:\/\/\S+)" result -> printfn "%A" result | _ -> () GroupMatches testString;;
06004
我试图实现的结果将如下所示:
06005
基本上是发现每个唯一匹配的映射,后跟在文本中找到特定匹配字符串的次数的计数.
如果你认为我要走错路,请随时提出一个完全不同的做法.我对Active Patterns和Regular Expression都有些新意,所以我不知道在哪里开始尝试解决这个问题.
我也想出了这个,基本上我将在C#中翻译成F#.
let testString = "http://www.bob.com http://www.b.com http://www.bob.com http://www.bill.com" let matches = let matchDictionary = new Dictionary<string,int>() for mtch in (Regex.Matches(testString,"(http:\/\/\S+)")) do for m in mtch.Captures do if(matchDictionary.ContainsKey(m.Value)) then matchDictionary.Item(m.Value) <- matchDictionary.Item(m.Value) + 1 else matchDictionary.Add(m.Value,1) matchDictionary
运行时返回这个:
06007
这基本上是我正在寻找的结果,但是我正在尝试学习这样做的功能方式,我认为应该包括活动模式.如果它比我的第一次尝试更有意义,可以尝试“功能化”这一点.
提前致谢,
短发
至于你的C#到F#的例子,你可以有功能的解决方案没有活动的模式,例如
let testString = "http://www.bob.com http://www.b.com http://www.bob.com http://www.bill.com" let matches input = Regex.Matches(input,"(http:\/\/\S+)") |> Seq.cast<Match> |> Seq.groupBy (fun m -> m.Value) |> Seq.map (fun (value,groups) -> value,(groups |> Seq.length)) //FSI output: > matches testString;; val it : seq<string * int> = seq [("http://www.bob.com",2); ("http://www.b.com",1); ("http://www.bill.com",1)]
更新
为什么这个特定的例子没有活动模式的工作正常的原因是因为1)你只是测试一个模式,2)你是动态处理的匹配.
对于一个现实世界的活动模式的例子,我们考虑一种情况,其中1)我们正在测试多个正则表达式,2)我们正在测试一个与多个组的正则表达式匹配.对于这些情况,我使用以下两个活动模式,这比您显示的第一个匹配活动模式(我不丢弃匹配中的第一个组)更为通用,我返回组对象的列表,而不仅仅是它们值 – 一个使用编译的正则表达式选项用于静态正则表达式模式,一个使用解释的regex选项用于动态正则表达式模式).因为.NET正则表达式API是如此的特征填充,您从活动模式返回的内容真的取决于您所发现的有用.但是返回一个列表的东西是好的,因为那时你可以在该列表中进行模式匹配.
let (|InterpretedMatch|_|) pattern input = if input = null then None else let m = Regex.Match(input,pattern) if m.Success then Some [for x in m.Groups -> x] else None ///Match the pattern using a cached compiled Regex let (|CompiledMatch|_|) pattern input = if input = null then None else let m = Regex.Match(input,pattern,RegexOptions.Compiled) if m.Success then Some [for x in m.Groups -> x] else None
还要注意这些活动模式如何考虑不匹配,而不是抛出异常.
好的,所以让我们来分析一下名字.我们有以下要求:
>必须有名字和姓氏
>可能有中间名
>首先,可选的中间和最后一个名称按照该顺序由单个空格分隔
>姓名的每个部分可以由至少一个或多个字母或数字的任意组合组成
>输入可能格式错误
首先我们将定义以下记录:
type Name = {First:string; Middle:option<string>; Last:string}
那么我们可以在一个解析名称的函数中非常有效地使用我们的正则表达式活动模式:
let parseName name = match name with | CompiledMatch @"^(\w+) (\w+) (\w+)$" [_; first; middle; last] -> Some({First=first.Value; Middle=Some(middle.Value); Last=last.Value}) | CompiledMatch @"^(\w+) (\w+)$" [_; first; last] -> Some({First=first.Value; Middle=None; Last=last.Value}) | _ -> None
注意我们在这里获得的关键优势之一,一般来说,模式匹配的情况是,我们能够同时测试输入与正则表达式匹配,并分解返回的组列表.