Attoparsec: Пропустить термины в квадратных скобках?

Я пытаюсь сделать большие файлы TSV с JSON в 5-м столбце, подходящие для импорта в mongoDB. В частности, я хочу изменить верхний уровень и только ключевые поля верхнего уровня на _id. Это то, что у меня есть до сих пор, кажется, работает, но медленно:

{-# LANGUAGE OverloadedStrings #-}

import System.Environment (getArgs)
import Data.Conduit.Binary (sourceFile, sinkFile)
import Data.Conduit
import qualified Data.Conduit.Text as CT
import qualified Data.Conduit.List as CL
import qualified Data.Text as T
import Data.Monoid ((<>))
import Data.Attoparsec.Text as APT
import Control.Applicative

main = do 
        (inputFile : outputFile : _) <- getArgs
        runResourceT $ sourceFile inputFile  
                $= CT.decode CT.utf8 $= CT.lines $= CL.map jsonify 
                $= CT.encode CT.utf8 $$ sinkFile outputFile

jsonify :: T.Text -> T.Text
jsonify = go . T.splitOn "\t"
        where 
        go (_ : _ : _ : _ : content : _) = case parseOnly keyTo_id content of
                Right res -> res <> "\n"
                _ -> ""
        go _ = ""

keyTo_id :: Parser T.Text 
keyTo_id = skipWhile(/='{') >> T.snoc <$>
        (T.cons <$> (char '{') 
                <*> (T.concat <$> many1 ( bracket 
                    <|> (string "\"key\":" >> return "\"_id\":") 
                    <|> APT.takeWhile1(\x -> x /= '{' && x /= '}' && x/= '"') 
                    <|> T.singleton <$> satisfy (/= '}')
                    )))  
        <*> char '}'        

bracket :: Parser T.Text        
bracket = T.cons <$> char '{' 
        <*> scan 1 test
     where
        test :: Int -> Char -> Maybe Int
        test 0 _ = Nothing        
        test i  '}'= Just (i-1)
        test i '{' = Just (i+1)
        test i _ = Just i

По данным профайлера 58,7% времени тратится на брекет, 19,6% на keyTo_id, 17,1% на основной.

Наверняка есть лучший способ вернуть термины в квадратных скобках без изменений, если скобки совпадают?

Я бегло просмотрел attoparsec-conduit, но понятия не имею, как использовать эту библиотеку, и даже не могу сказать, можно ли ее использовать именно для этого.

РЕДАКТИРОВАТЬ: Обновлен код. Данные взяты с openlibrary.org, т.е. грамм. http://openlibrary.org/data/ol_dump_authors_latest.txt.gz


person FAWS    schedule 03.10.2012    source источник


Ответы (1)


Используйте функцию scan. Это позволяет вам сканировать строку, сохраняя состояние. В вашем случае состояние будет числом — разницей открывающих и закрывающих фигурных скобок, с которыми вы сталкивались до сих пор. Когда ваше состояние равно 0, это означает, что фигурные скобки совпадают внутри текущей подстроки.

Хитрость в том, что вы не разбираете и не восстанавливаете строку таким образом, поэтому она должна быть быстрее.

Кроме того, вы можете повысить производительность даже с вашим текущим алгоритмом, используя отложенный текст — функция concat будет работать более эффективно.

person Roman Cheplyaka    schedule 03.10.2012
comment
Я попробовал сканирование, оно улучшило эффективность использования пространства примерно на 15% и скорость примерно на 5%. Я не уверен, как реализовать ваше второе предложение, поскольку, похоже, не существует версий функций takeWhile1 или Conduit.Text для Lazy Text, поэтому, похоже, мне придется конвертировать туда и обратно несколько раз. - person FAWS; 04.10.2012