9

我正在通过使用活动模式编写递归下降解析器来学习 F#。

由于我的所有规则或部分活动模式我需要以不同的方式组合它们,但我对将活动模式作为参数传递的语法感到非常沮丧。

以下示例显示了我遇到的麻烦:

// Combines two patterns by chaining them.
let (|Chain|_|) (|Pattern1|_|) (* Should I use pipes here? *) (|Pattern2|_|) data =
    match data with
    |Pattern1 result ->
        match result with
        |Pattern2 result2 -> Some result2
        |_ -> None
    |_ -> None 

// Stupid test patterns
let (|IfBiggerThan10ThenDouble|_|) value = if value > 10 then Some (value*2) else None
let (|IfLessThan100ThenDouble|_ |) value = if value < 100 then Some (value*2) else None

match 20 with
// Do I need pipes here?
|Chain (IfBiggerThan10ThenDouble IfLessThan100ThenDouble) value -> printfn "%A" value // Should print 80
| _ -> printfn "Did not match"

我的主要困惑似乎是关于“|” 操作员。有时它似乎是模式类型的一部分,有时是名称的一部分。

4

1 回答 1

14

您实际上不需要实现自己的模式链,因为您可以直接嵌套模式,从而为您提供所需的结果:

match 20 with
| IfBiggerThan10ThenDouble(IfLessThan100ThenDouble value) -> printfn "%A" value
| _ -> printfn "Did not match"

这将首先调用IfBiggerThan10ThenDouble计算20*2并将值传递给嵌套模式的模式IfLessThan100ThenDouble。这再次将值加倍并将其绑定到value符号(当它成功时)。

也就是说,您的Chain模式实现确实有效,可以这样调用:

match 20 with
| Chain (|IfBiggerThan10ThenDouble|_|) (|IfLessThan100ThenDouble|_|) value -> 
    printfn "%A" value // Should print 80
| _ -> printfn "Did not match"

一般来说,活动模式(|P|_|)实际上只是一个具有特殊名称的函数。您可以将其视为普通函数并通过编写来调用它,(|P|_|) argument也可以将其视为一个值并将其作为参数传递给其他函数或参数化的活动模式。如果您实现Chain为采用普通函数的模式,您的代码将起作用:

let (|Chain|_|) f g data =
    f data |> Option.bind (fun r -> g data)

然后Chain <arg1> <arg2> <pat>只是用两个函数作为参数调用参数化的活动模式。调用时,它将结果绑定到模式<pat>。在上面的示例中,两个参数是表示模式的函数值(这些可以是普通函数,但由于语法限制,不能是 lambda 函数)。

于 2014-03-25T13:24:18.340 回答