Рекурсивно вернуть все слова из файла .txt, используя attoparsec - PullRequest
0 голосов
/ 04 мая 2018

Я довольно новичок в Haskell, и я только начинаю изучать, как работать с attoparsec для разбора огромных кусков английского текста из файла .txt. Я знаю, как получить количество слов в файле .txt без использования attoparsec, но я вроде как застрял с attoparsec. Когда я запускаю свой код ниже, скажем,

"Привет, мир, я Эллиот Андерсон. \ NА я мистер Робот. \ N"

Я только вернусь:

Мир, я Эллиот Андерсон. \ nИ я мистер Робот. \ n "(Проза {слово = "Hello"})

Это мой текущий код:

{-# LANGUAGE OverloadedStrings #-}
import Control.Exception (catch, SomeException)
import System.Environment (getArgs)
import Data.Attoparsec.Text
import qualified Data.Text.IO as Txt
import Data.Char
import Control.Applicative ((<*>), (*>), (<$>), (<|>), pure)

{-
This is how I would usually get the length of the list of words in a .txt file normally.

countWords :: String -> Int
countWords input = sum $ map (length.words) (lines input)

-}

data Prose = Prose {
  word :: String
} deriving Show

prose :: Parser Prose
prose = do
  word <- many' $ letter
  return $ Prose word

main :: IO()
main = do
  input <- Txt.readFile "small.txt"
  print $ parse prose input

Кроме того, как я могу получить целое число слов, позже? Кроме того, какие-либо предложения о том, как начать с attoparsec?

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Вы на правильном пути! Вы написали парсер (prose), который читает одно слово: many' letter распознает последовательность букв.

Итак, теперь, когда вы выяснили, как разбирать одно слово, ваша задача - увеличить его, чтобы разобрать последовательность слов, разделенных пробелами. Вот что sepBy делает: p `sepBy` q многократно запускает парсер p с разбросанным парсером q.

Итак, парсер последовательности слов выглядит примерно так (я позволил себе переименовать ваш prose в word):

word = many letter
phrase = word `sepBy` some space  -- "some" runs a parser one-or-more times

ghci> parseOnly phrase "wibble wobble wubble"  -- with -XOverloadedStrings
Right ["wibble","wobble","wubble"]

Теперь, phrase, составленный из letter и space, умрет от непробельных непробельных символов, таких как ' и .. Я оставлю это вам, чтобы выяснить, как это исправить. (Как подсказка, вам, вероятно, потребуется изменить many letter на many (letter <|> ...), в зависимости от того, как именно вы хотите, чтобы он вел себя на различных знаках препинания.)

0 голосов
/ 04 мая 2018

У вас уже неплохое начало - вы можете разобрать слово.
Далее вам нужен Parser [Prose], который можно выразить, комбинируя ваш prose парсер с другим, который потребляет "непрозрачные" части, используя sepBy или sepBy1, который вы можете посмотреть в Data.Attoparsec.Text документация.

Отсюда самый простой способ подсчитать количество слов - просто получить длину полученного вами [Prose].

EDIT:

Вот минимальный рабочий пример. Бегунок Parser был заменен на parseOnly, чтобы позволить игнорировать остаточный ввод, что означает, что завершающее неслово не заставит синтаксический анализатор стать cray-cray.

{-# LANGUAGE OverloadedStrings #-}

module Atto where

--import qualified Data.Text.IO as Txt
import Data.Attoparsec.Text
import Control.Applicative ((*>), (<$>), (<|>), pure)

import qualified Data.Text as T

data Prose = Prose {
  word :: String
} deriving Show

optional :: Parser a -> Parser ()
optional p = option () (try p *> pure ())

-- Modified to disallow empty words, switched to applicative style
prose :: Parser Prose
prose = Prose <$> many1' letter

separator :: Parser ()
separator = many1 (space <|> satisfy (inClass ",.'")) >> pure ()

wordParser :: String -> [Prose]
wordParser str = case parseOnly wp (T.pack str) of
    Left err -> error err
    Right x -> x
    where
        wp = optional separator *> prose `sepBy1` separator

main :: IO ()
main = do
  let input = "Hello World, I am Elliot Anderson. \nAnd I'm Mr.Robot.\n"
  let words = wordParser input
  print words
  print $ length words

Предоставленный синтаксический анализатор не дает тот же результат, что и concatMap words . lines, поскольку он также разбивает слова на .,'. Изменение этого поведения остается простым упражнением.

Надеюсь, это поможет! :)

...