首页 > 解决方案 > F#如何定义“递归”变量

问题描述

我正在使用 FParsec 库为伪语言编写一个解析器,我有一个语句解析器,它是在所有可能的语句之间选择一个块解析器,它解析一系列语句,直到一个“结束”关键字,现在我想编写一个“循环”结构,问题是,循环本身是一个语句,并且包含一个块,这导致了一个递归形式的定义,F# 不喜欢我之前在 C# 中编写了一个解析器,并且通过这很容易,因为每个解析器都是一个函数,并且它们可以相互调用,而不管它们是在之前还是之后定义的

所以我想知道如何在 F# 中解决这个问题

这是提到的解析器:

// parses a list of statements, separated by 1 or more newlines, (pre|post)fixed by any amount of whitespace, and finishes parsing upon reaching an "end" keyword
let block endtag = many1Till (statement .>> nl) (skipString endtag) // at this moment, statement is undefined

// parses a loop structure; the keyword loop, followed by an identifier for the iteration variable, followed by an expression which evaluates to the amount iterations which should be executed, followed by a block closed with "endloop"
let loop = skipString "loop" >>. ws1 >>. id .>> ws1 .>>. expr .>> nl .>>. block "endloop" |>> fun ((i, n), s) -> Loop (i, n, s)

// parses any statement, pre or post fixed by any amount of whitespace
let statement = spaces >>. choice [writeline; write; comment; definition; loop; sleep; assignment] .>> spaces

标签: functional-programmingf#fparsec

解决方案


FParsec 通过createParserForwardedToRef. 本教程中描述的 JSON 解析器展示了如何使用它。以下是相关信息的摘录:

JSON 列表和对象的语法规则是递归的,因为任何列表或对象都可以包含任何类型的 JSON 值。因此,为了为列表和对象语法规则编写解析器,我们需要一种方法来为任何类型的 JSON 值引用解析器,即使我们还没有构建这个解析器。就像在计算中经常发生的那样,我们可以通过引入额外的间接来解决这个问题:

let jvalue, jvalueRef = createParserForwardedToRef<Json, unit>()

正如您可能从名称中猜到的那样,createParserForwardedToRef创建一个解析器 ( jvalue),它将所有调用转发到引用单元 ( jvalueRef) 中的解析器。最初,参考单元拥有一个虚拟解析器,但由于参考单元是可变的,一旦我们完成构造,我们可以稍后用实际值解析器替换虚拟解析器。


推荐阅读