F # Как токенизировать пользовательский ввод: разделять числа, единицы, слова? - PullRequest
3 голосов
/ 11 января 2011

Я довольно новичок в F #, но провел последние несколько недель, читая справочные материалы.Я хочу обработать предоставленную пользователем строку ввода, идентифицируя и разделяя составляющие элементы.Например, для этого ввода:

Отель XYZ: 6 ночей по 220EUR / ночь плюс налог в размере 17,5%

вывод должен выглядеть примерно как список кортежей:

[("XYZ", Word);(«Гостиница:», Слово);
(«6», Номер);(«Ночи», Word);
(«at», Оператор);(«220», Number);
(«EUR», CurrencyCode);("/", Оператор);(«ночь», слово);
(«плюс», оператор);(«17,5», номер);("%", Процентов);(«налог», Word)] * ​​1014 *

Поскольку я имею дело с пользовательским вводом, это может быть что угодно.Таким образом, ожидать от пользователей соблюдения грамматики не может быть и речи.Я хочу идентифицировать числа (могут быть целыми числами, числами с плавающей запятой, отрицательными ...), единицы измерения (необязательно, но могут включать физические единицы СИ или имперские единицы, коды валют, значения, такие как "ночь / с" в моем примере)математические операторы (в виде математических символов или слов, в том числе "at", "per", "of", "discount" и т. д.) и другие слова.

У меня сложилось впечатление, что я должен использовать активное сопоставление с образцом - это правильно?- но я не совсем уверен, как начать.Любые указатели на соответствующий справочный материал или подобные примеры были бы хорошими.

Ответы [ 2 ]

5 голосов
/ 11 января 2011

Я собрал пример, используя библиотеку 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 голос
/ 11 января 2011

Похоже, что вы действительно хотите, это просто лексер. Хорошей альтернативой FSParsec будет FSLex. (Хорошее вводное руководство, немного устаревшее, можно найти в моем старом блоге здесь .) Используя 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

Это должно, по крайней мере, начать вас. Но обратите внимание, что по мере того, как ваш вклад становится более «формальным», полезность вашего лексинга также возрастает. (И если вы принимаете только очень специфические данные, вы можете использовать правильный синтаксический анализатор и покончить с этим!)

...