2011-01-11 6 views
3

私はF#をかなり新しくしていますが、ここ数週間は参考資料を読んでいます。私は、ユーザー提供の入力文字列を処理して、構成要素を識別して分離したいと考えています。F#ユーザー入力をトークン化する方法:数字、単位、単語を区切る?

XYZホテル:220EUR /夜 プラス17.5%の税

で6泊の出力は、タプルのリストのようなもののようになります。

例えば、この入力用[( "XYZ"、Word); (「ホテル:」、ワード);
( "6"、Number)。 ( "夜"、Word);
( "at"、演算子); ( "220"、Number);
( "EUR"、CurrencyCode); ( "/"、 オペレータ)。 ( "夜"、ワード);
( "plus"、Operator); ( "17.5"、 番号); ( "%"、PerCent); ( "tax"、 Word)]

私はユーザー入力を処理しているので、何でもかまいません。したがって、ユーザが文法に従うことを期待することは問題ではない。私は数値(整数、浮動小数点数、負数など)、小単位(任意であるが、SIまたはImperial物理単位、通貨コード、私の例では「夜/ 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は良い解決策であると言っていますか? –

+0

@David - 正確には、FParsecはテキスト解析ツールとして書かれており、それは柔軟なものです。 – ChaosPandion

+0

私はあなたの例を走らせて、私は最も感銘を受けました。FParsecのドキュメントを読む必要があるので、必要に応じてこれを知的に修正することができます。 –

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 

これは少なくとも開始する必要があります。しかし、あなたの入力がより正式なものになるにつれて、あなたのレキシングの有用性も増していくことに注意してください。 (そして、あなたが非常に特定の入力だけを受け入れるならば、適切なパーサーを使ってそれを使って完了できます!)

関連する問題