f# - 使用ParserResult

标签 f# fparsec

下面的示例代码看起来很好用:

open FParsec
let capitalized : Parser<unit,unit> =(asciiUpper >>. many asciiLower >>. eof)
let inverted : Parser<unit,unit> =(asciiLower >>. many asciiUpper >>. eof)
let capsOrInvert =choice [capitalized;inverted]

然后,您可以执行以下操作:
run capsOrInvert "Dog";;
run capsOrInvert "dOG";;

并获得成功或:
run capsOrInvert "dog";;

并失败了。

现在我有了一个ParserResult,该如何处理呢?例如,向后打印字符串?

最佳答案

您的代码有几个明显的问题。

首先,正如@scrwtp的答案所指出的,您的解析器将返回unit原因如下:运算符(>>.)仅返回右侧内部解析器返回的结果。另一方面,(.>>)将返回左分析器的结果,而(.>>.)将返回左和右两个元组。

因此,parser1 >>. parser2 >>. eof本质上是(parser1 >>. parser2) >>. eof
parens中的代码将完全忽略parser1的结果,然后第二个(>>.)将忽略parens中的解析器的整个结果。最后,eof返回unit,并将返回此值。

您可能需要返回一些有意义的数据,例如解析的字符串。最简单的方法是:

let capitalized = (asciiUpper .>>. many asciiLower .>> eof)

注意运算符(operator)。inverted的代码可以类似的方式完成。

该解析器的类型为Parser<(char * char list), unit>,第一个字符的元组和所有其余字符的元组,因此您可能需要将它们合并回。有几种方法可以做到这一点,这是一种:
let mymerge (c1: char, cs: char list) = c1 :: cs // a simple cons
let pCapitalized = capitalized >>= mymerge

该代码的优点在于,您的 mymerge是正常函数,与普通char一起使用时,它对解析器一无所知。它仅适用于数据,其余的由(>>=)运算符完成。

注意,pCapitalized也是一个解析器,但是它返回一个char list

没有什么可以阻止您从应用进一步的转换。正如您提到的向后打印字符串:
let pCapitalizedAndReversed =
    capitalized
    >>= mymerge
    >>= List.rev

我已经为此目的编写了代码。在不同的行中,您看到域数据的逐渐过渡,仍在Parser 范式之内。这是一个重要的考虑因素,因为例如任何后续转换都可能“出于某种原因”确定数据是错误的,并引发解析异常。或者,可以将其与其他解析器合并。

一旦您的域数据(已解析的单词)完成,就可以提取另一个答案中提到的结果。

小调。 choice仅对于两个解析器是多余的。请改用(<|>)。根据经验,仔细选择解析器组合器非常重要,因为在核心解析器逻辑内部进行错误的选择很容易使解析器显着变慢。
有关更多详细信息,请参见FParsec Primitives

关于f# - 使用ParserResult,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40619561/

相关文章:

api - 如何在 F# 中隐藏方法?

.net - 在 .NET 中表示不可变列表的最佳方式是什么?

c# - 如何与 .net 中的另一个项目链接

f# - F# 中 "==>"语法的含义

reflection - 定义具有非常多消息类型的消息传递域

parsing - 提高 FParsec 解析器的可读性

parsing - 如何在 VS2013 中运行 FParsec

parsing - 如何解决 FParsec 错误 "The combinator ' much' 已应用于解析器,该解析器无需消耗即可成功......”

f# - 使用 FParsec 解析变量声明