我对F#相当新,但过去几周都在阅读参考资料。我希望处理用户提供的输入字符串,识别和分离组成元素。例如,对于此输入:
XYZ酒店:入住6晚,每晚220欧元 加17.5%税
输出应该类似于元组列表:
[(“XYZ”,Word); (“酒店:”,Word);
(“6”,数字); (“夜”,Word);
(“at”,运营商); (“220”,数字);
(“EUR”,CurrencyCode); ( “/”, 运营商); (“夜”,Word);
(“加”,运营商); ( “17.5”, 数); (“%“, 百分); (“税”, Word)]
由于我正在处理用户输入,它可能是任何东西。因此,期望用户遵守语法是不可能的。我想识别数字(可能是整数,浮点数,负数......),度量单位(可选,但可以包括SI或Imperial物理单位,货币代码,例如“night / s”中的计数) ,数学运算符(作为数学符号或包括“at”“per”,“of”,“discount”等的单词)以及所有其他单词。
我的印象是我应该使用主动模式匹配 - 这是正确的吗? - 但我不确定如何开始。任何指向适当参考材料或类似示例的指针都会很棒。
答案 0 :(得分:5)
我使用FParsec库汇总了一个示例。这个例子根本不健壮,但它给出了如何使用FParsec的非常好的图片。
type Element =
| Word of string
| Number of string
| Operator of string
| CurrencyCode of string
| PerCent of string
let parsePerCent state =
(parse {
let! r = pstring "%"
return PerCent r
}) state
let currencyCodes = [|
pstring "EUR"
|]
let parseCurrencyCode state =
(parse {
let! r = choice currencyCodes
return CurrencyCode r
}) state
let operators = [|
pstring "at"
pstring "/"
|]
let parseOperator state =
(parse {
let! r = choice operators
return Operator r
}) state
let parseNumber state =
(parse {
let! e1 = many1Chars digit
let! r = opt (pchar '.')
let! e2 = manyChars digit
return Number (e1 + (if r.IsSome then "." else "") + e2)
}) state
let parseWord state =
(parse {
let! r = many1Chars (letter <|> pchar ':')
return Word r
}) state
let elements = [|
parseOperator
parseCurrencyCode
parseWord
parseNumber
parsePerCent
|]
let parseElement state =
(parse {
do! spaces
let! r = choice elements
do! spaces
return r
}) state
let parseElements state =
manyTill parseElement eof state
let parse (input:string) =
let result = run parseElements input
match result with
| Success (v, _, _) -> v
| Failure (m, _, _) -> failwith m
答案 1 :(得分:1)
听起来你真正想要的只是一个词法分析器。 FSParsec的一个很好的替代品是FSLex。 (好的介绍教程,albiet有点过时,可以在我的旧博客上找到here。)使用FSLex,你可以输入你的输入文字:
XYZ Hotel: 6 nights at 220EUR / night plus 17.5% tax
将其正确地标记为:
[ Word("XYZ"); Hotel; Int(6); Word("nights"); Word("at"); Int(220); EUR; ... ]
下一步,一旦你有一个令牌列表,就是做一些形式的模式匹配/分析来提取语义信息(我假设你正是这样做的)。使用规范化的令牌流,它应该如下所示:
let rec processTokenList tokens =
match tokens with
| Float(x) :: Keyword("EUR") :: rest -> // Dollar amount x
| Word(x) :: Keyword("Hotel") :: rest -> // Hotel x
| hd :: rest -> // Couldn't find anything interesting...
processTokenList rest
至少应该让你开始。但请注意,随着您的输入变得更“正式”,您的lexing的用处也将如此。 (如果你只接受一个非常具体的输入,那么你可以使用一个合适的解析器并完成它!)