相互递归让绑定

时间:2015-06-28 20:59:00

标签: recursion f#

我正在尝试实现类似这样的解析器:

Panel

最后的函数在其定义中是相互递归的。 open System type ParseResult<'a> = { Result : Option<'a>; Rest : string } let Fail = fun input -> { Result = None; Rest = input } let Return a = fun input -> { Result = Some a; Rest = input } let ThenBind p f = fun input -> let r = p input match r.Result with | None -> { Result = None; Rest = input } // Recreate the result since p returns a ParseResult<'a> | _ -> (f r.Result) r.Rest let Then p1 p2 = ThenBind p1 (fun r -> p2) let Or p1 p2 = fun input -> let r = p1 input match r.Result with | None -> p2 input | _ -> r let rec Chainl1Helper a p op = Or <| ThenBind op (fun f -> ThenBind p (fun y -> Chainl1Helper (f.Value a y.Value) p op)) <| Return a let Chainl1 p op = ThenBind p (fun x -> Chainl1Helper x.Value p op) let rec Chainr1 p op = ThenBind p (fun x -> Or (ThenBind op (fun f -> ThenBind (Chainr1 p op) (fun y -> Return (f.Value x.Value y.Value)))) (Return x.Value)) let Next = fun input -> match input with | null -> { Result = None; Rest = input } | "" -> { Result = None; Rest = input } | _ -> { Result = Some <| char input.[0..1]; Rest = input.[1..] } let Sat predicate = ThenBind Next (fun n -> if predicate n.Value then Return n.Value else Fail) let Digit = ThenBind (Sat Char.IsDigit) (fun c -> Return <| float c.Value) let rec NatHelper i = Or (ThenBind Digit (fun x -> NatHelper (float 10 * i + x.Value) )) (Return i) let Nat = ThenBind Digit (fun d -> NatHelper d.Value) let LiteralChar c = Sat (fun x -> x = c) let rec Literal input token = match input with | "" -> Return token | _ -> Then (LiteralChar <| char input.[0..1]) (Literal input.[1..] token) let AddSub = Or <| ThenBind (LiteralChar '+') (fun c -> Return (+)) <| ThenBind (LiteralChar '-') (fun c -> Return (-)) let MulDiv = Or <| ThenBind (LiteralChar '*') (fun c -> Return (*)) <| ThenBind (LiteralChar '/') (fun c -> Return (/)) let Exp = ThenBind (LiteralChar '^') (fun c -> Return ( ** )) let rec Expression = Chainl1 Term AddSub and Term = Chainl1 Factor MulDiv and Factor = Chainr1 Part Exp and Part = Or Nat Paren and Paren = Then <| LiteralChar '(' <| ThenBind Expression (fun e -> Then (LiteralChar ')') (Return e.Value)) 的定义取决于ExpressionTerm取决于Factor,取决于Part,这取决于Paren,这取决于Expression }。

当我尝试编译它时,我得到一个关于相互递归定义的错误,其中包含使Expression延迟或函数的建议。我尝试了这两个,我得到了一个神秘的InvalidOperationException,两者都说明了ValueFactory试图访问Value属性。

1 个答案:

答案 0 :(得分:3)

通常,F#允许您使用let rec .. and ..不仅用于定义相互递归函数,还用于定义相互递归值。这意味着您可以编写如下内容:

let rec Expression = Chainl1 Term AddSub
and Paren =
    Then
        <| LiteralChar '('
        <| ThenBind Expression (fun e ->
            Then (LiteralChar ')') (Return e.Value))
and Part = Or Nat Paren
and Factor = Chainr1 Part Exp
and Term = Chainl1 Factor MulDiv

但是,这仅在不立即计算计算时才起作用(因为那时递归定义没有意义)。这在很大程度上取决于您在此处使用的库(或其他代码)。但你可以试试上面的内容,看看是否有效 - 如果没有,你需要提供更多细节。

编辑在更新的示例中,递归定义中存在立即循环。您需要使用fun _ -> ...延迟定义的某些部分,以便不是所有内容都需要立即进行评估。在您的示例中,您可以通过在Then的定义中将ThenBind替换为Paren来实现此目的:

let rec Expression = Chainl1 Term AddSub
and Term = Chainl1 Factor MulDiv
and Factor = Chainr1 Part Exp
and Part = Or Nat Paren
and Paren =
    ThenBind
      (LiteralChar '(')
      (fun _ -> ThenBind Expression (fun e ->
            Then (LiteralChar ')') (Return e.Value)))