F#正则表达匹配链

时间:2016-07-18 20:04:52

标签: regex f# pattern-matching operators

由于我对F#的正则表达式实现并不完全满意,我想实现一个所谓的正则表达式链。它基本上如下工作:

将检查给定字符串s,无论它是否与第一个模式匹配。如果是,它应该执行与第一个模式相关的功能。如果没有,则应继续下一个。

我尝试按如下方式实现它:

let RegexMatch ((s : string, c : bool), p : string, f : GroupCollection -> unit) =
    if c then
        let m = Regex.Match(s, p)
        if m.Success then
            f m.Groups
            (s, false)
        else (s, c)
    else (s, c)


("my input text", true)
|> RegexMatch("pattern1", fun g -> ...)
|> RegexMatch("pattern2", fun g -> ...)
|> RegexMatch("pattern3", fun g -> ...)
|> .... // more patterns
|> ignore

问题是,这段代码无效,因为正向管道运算符似乎没有管道元组或者不喜欢我的实现' design'。

我的问题是:我可以轻松修复上面的代码,还是应该实现其他类型的正则表达式链?

2 个答案:

答案 0 :(得分:6)

你的函数RegexMatch不支持管道,因为它有参数元组。

首先,看一下管道的定义:

let (|>) x f = f x

由此可以清楚地看到这个表达式:

("text", true)
|> RegexMatch("pattern", fun x -> ...)

等同于:

RegexMatch("pattern", fun x -> ...) ("text", true)

这是否符合您的功能签名?显然不是。在您的签名中,text / bool对首先出现,并且是参数的三倍,以及模式和功能。

为了使它工作,你需要采取"管道"咖喱形式和最后的参数:

let RegexMatch p f (s, c) = ...

然后你可以做管道:

("input", true)
|> RegexMatch "pattern1" (fun x -> ...)
|> RegexMatch "pattern2" (fun x -> ...)
|> RegexMatch "pattern3" (fun x -> ...)

顺便说一句,我必须注意你的方法不是很好,嗯,功能性。您将整个逻辑基于副作用,这将使您的程序无法组合并且难以测试,并且可能容易出错。你并没有从F#中获益,有效地将它作为#34; C#使用更好的语法"。

此外,实际上有很多研究方法可以达到你想要的效果。例如,查看Railway-oriented programming(也称为monadic计算)。

答案 1 :(得分:1)

对我而言,这听起来像你要实现的是Active Patterns

使用活动模式,您可以使用常规模式匹配语法来匹配RegEx模式:

let (|RegEx|_|) p i =
  let m = System.Text.RegularExpressions.Regex.Match (i, p)
  if m.Success then
    Some m.Groups
  else
    None

[<EntryPoint>]
let main argv = 
  let text = "123"
  match text with
  | RegEx @"\d+" g -> printfn "Digit: %A" g
  | RegEx @"\w+" g -> printfn "Word : %A" g
  | _              -> printfn "Not recognized"
  0

另一种方法是使用Fyodor所指的铁路导向编程:

type RegexResult<'T> = 
  | Found     of  'T
  | Searching of  string

let lift p f = function
  | Found v     -> Found v
  | Searching i -> 
    let m = System.Text.RegularExpressions.Regex.Match (i, p)
    if m.Success then
      m.Groups |> f |> Found
    else
      Searching i

[<EntryPoint>]
let main argv = 
  Searching "123"
  |> lift @"\d+" (fun g -> printfn "Digit: %A" g)
  |> lift @"\w+" (fun g -> printfn "Word : %A" g)
  |> ignore
  0