2011-01-11 54 views
3

我對F#相當陌生,但是在過去的幾個星期裏閱讀了參考資料。我希望處理用戶提供的輸入字符串,識別和分離組成元素。例如,對於此輸入:F#如何標記用戶輸入:分隔數字,單位,單詞?

XYZ酒店:6晚住宿220EUR /夜 加上17.5%的稅

輸出應該類似於像元組的列表:

[(「XYZ」,單詞); (「酒店:」,Word);
(「6」,Number); (「晚上」,單詞);
(「at」,Operator); (「220」,Number);
(「EUR」,CurrencyCode); (「/」, 運營商); (「夜」,Word);
(「plus」,Operator); (「17.5」, 編號); (「%「, 百分); (「稅」, Word)]

由於我正在處理用戶輸入,它可能是任何東西。因此,期望用戶遵守語法是不可能的。我想識別這些數字(可以是整數,浮點數,負數......),度量單位(可選,但可以包括SI或帝國物理單位,貨幣代碼,例如我的示例中的「night/s」), ,數學運算符(如數學符號或包含「at」,「per」,「of」,「discount」等的單詞)等所有單詞。

我的印象是我應該使用主動模式匹配 - 是對的嗎? - 但我不確定如何開始。任何指向適當參考資料或類似例子的指針都會很棒。

回答

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 
+0

這是一個全面的響應。你是說FParsec是一個很好的解決方案,即使我正在處理自由格式的文本 - 不是嚴格的語法? – 2011-01-11 05:27:20

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 

至少應該讓您開始。但請注意,隨着您的輸入變得更加「正式」,您的練習的實用性也會隨之提高。 (如果你只接受一個非常具體的輸入,那麼你可以使用一個合適的解析器並完成它!)