下面的示例代码似乎很好用:
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 Code Online (Sandbox Code Playgroud)
然后你可以这样做:
run capsOrInvert "Dog";;
run capsOrInvert "dOG";;
Run Code Online (Sandbox Code Playgroud)
并获得成功或:
run capsOrInvert "dog";;
Run Code Online (Sandbox Code Playgroud)
并失败.
既然我有一个ParserResult,我该怎么做呢?例如,向后打印字符串?
ParserResult是一个受歧视的联盟.你只需匹配Success和Failure案例.
let r = run capsOrInvert "Dog"
match r with
| Success(result, _, _) -> printfn "Success: %A" result
| Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg
Run Code Online (Sandbox Code Playgroud)
但这可能不是你对你的情况感到棘手的事情.
关于你的Parser<unit, unit>类型的事情是解析的值是类型unit(第一个类型参数Parser).这意味着这个解析器并没有真正产生任何合理的输出供你使用 - 它只能告诉你它是否可以解析一个字符串(在这种情况下你得到一个Success ((), _, _)- 带有单一的值类型unit)或不.
你期望从这个解析器中获得什么?
编辑:这听起来很接近你想要的,或者至少你应该能够从中获取一些指针.capitalized接受大写字符串,inverted接受已被反转的大写字符串,并将它们作为解析器逻辑的一部分进行反转.
let reverse (s: string) =
System.String(Array.rev (Array.ofSeq s))
let capitalized : Parser<string,unit> =
(asciiUpper .>>. manyChars asciiLower)
|>> fun (upper, lower) -> string upper + lower
let inverted : Parser<string,unit> =
(manyChars asciiLower .>>. asciiUpper)
|>> fun (lower, upper) -> reverse (lower + string upper)
let capsOrInvert = choice [capitalized;inverted]
run capsOrInvert "Dog"
run capsOrInvert "doG"
run capsOrInvert "dog"
Run Code Online (Sandbox Code Playgroud)
您的代码有几个值得注意的问题.
首先,正如在@scrwtp的回答中所注意到的,你的解析器会返回unit.原因如下:operator (>>.)只返回右内部解析器返回的结果.在另一方面,(.>>)将返回的结果左边的解析器,而(.>>.)会返回两者的元组左和右的.
所以,parser1 >>. parser2 >>. eof基本上是(parser1 >>. parser2) >>. eof.
parens中的代码完全忽略了结果parser1,然后第二个(>>.)忽略了parens中解析器的整个结果.最后,eof返回unit,并返回此值.
您可能需要返回一些有意义的数据,例如解析后的字符串.最简单的方法是:
let capitalized = (asciiUpper .>>. many asciiLower .>> eof)
Run Code Online (Sandbox Code Playgroud)
介意运营商.
代码inverted可以以类似的方式完成.
这个解析器是类型Parser<(char * char list), unit>,第一个字符的元组和所有剩余的,所以你可能需要将它们合并.有几种方法可以做到这一点,这里有一个:
let mymerge (c1: char, cs: char list) = c1 :: cs // a simple cons
let pCapitalized = capitalized >>= mymerge
Run Code Online (Sandbox Code Playgroud)
这段代码的优点在于你mymerge是一个正常的函数,使用普通函数char,它对解析器一无所知.它只适用于数据,(>>=)操作员完成其余的工作.
注意,pCapitalized也是一个解析器,但它返回一个char list.
没有什么可以阻止你应用进一步的过渡.正如您所提到的向后打印字符串:
let pCapitalizedAndReversed =
capitalized
>>= mymerge
>>= List.rev
Run Code Online (Sandbox Code Playgroud)
我是出于这个目的编写代码的.在不同的行中,您会看到域数据的逐渐过渡,仍然在Parser的范例内.这是一个重要的考虑因素,因为任何后续转换都可能由于某种原因"决定"数据是坏的并且例如引发解析异常.或者,也可以将其与其他解析器合并.
一旦您的域数据(解析出的单词)完成,您就会提取另一个答案中提到的结果.
小调.choice只有两个解析器是多余的.请(<|>)改用.根据经验,仔细选择解析器组合器很重要,因为核心解析器逻辑内部的错误选择很容易使解析器显着减慢.
有关更多详细信息,请参见FParsec Primitives.