F#如何标记用户输入:分隔数字,单位,单词?

时间:2011-01-11 03:08:24

标签: f# pattern-matching

我对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”等的单词)以及所有其他单词。

我的印象是我应该使用主动模式匹配 - 这是正确的吗? - 但我不确定如何开始。任何指向适当参考材料或类似示例的指针都会很棒。

2 个答案:

答案 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的用处也将如此。 (如果你只接受一个非常具体的输入,那么你可以使用一个合适的解析器并完成它!)