2014-07-10 2 views
4

내 유형 :에서 첫 번째 줄을 건너 뛰는 파이프 - attoparsec

data Test = Test { 
a :: Int, 
b :: Int 
} deriving (Show) 

내 파서 :

testParser :: Parser Test 
testParser = do 
    a <- decimal 
    tab 
    b <- decimal 
    return $ Test a b 

tab = char '\t' 

이제 첫 줄을 건너 뛸하기 위해, 나는 같은 것을 할 :

import qualified System.IO as IO  

parser :: Parser Test 
parser = manyTill anyChar endOfLine *> testParser 

main = IO.withFile testFile IO.ReadMode $ \testHandle -> runEffect $ 
     for (parsed (parser <* endOfLine) (fromHandle testHandle)) (lift . print) 

그러나 위의 parser 함수는 모든 대체 링크를 건너 뜁니다 (이는 명백합니다). 파이프 생태계 (Producer 값을 생성해야합니다)와 같은 방식으로 첫 번째 줄을 건너 뛰는 방법. 이것은 내가 원하지 않는 명백한 해결책입니다 (아래 코드는 testParser를 다음과 같이 수정 한 경우에만 작동합니다. 그것은 하나의 값 대신 전체 [Test]을 반환하기 때문에) 줄 바꿈을 읽어

tests :: Parser [Test] 
tests = manyTill anyChar endOfLine *> 
     many1 testParser 

어떤 아이디어이 문제를 해결하기 위해? 첫 번째 줄은 유효한 Test를 포함하지 않는 경우

+0

을 그건 그렇고, 당신은 Test'와'Link''전환 할 수 있습니다. – Zeta

+0

@Zeta 죄송합니다. 제 실수입니다. 'Test'가되도록 업데이트되었습니다. (원래의 데이터 구조는 사실 더 많은 필드를 가진'Link'입니다.이 질문에 대해서는'Test'로 간단하게했습니다.) – Sibi

답변

5

이 같은 일정한 공간에 효율적으로 제 떨어질 줄 수

을 이 같은 Producer를 분석하기 전에
import Lens.Family (over) 
import Pipes.Group (drops) 
import Pipes.ByteString (lines) 
import Prelude hiding (lines) 

dropLine :: Monad m => Producer ByteString m r -> Producer ByteString m r 
dropLine = over lines (drops 1) 

당신은 당신의 ProducerdropLine을 적용 할 수

main = IO.withFile testFile IO.ReadMode $ \testHandle -> runEffect $ 
    let p = dropLine (fromHandle testHandle) 
    for (parsed (parser <* endOfLine) p) (lift . print) 
+0

라인을 없애고 싶지만 라인을 기다리는 것은 무엇입니까? '라인 오버 (drops o)'보다 나은 방법이 있을까요? – Igor

+0

@Igor'Pipes.Prelude'를 제외하고,'pipes' 생태계는 임의의 길이가 될 수 있기 때문에 전체 라인을 메모리로 읽어들이는 것을 꺼립니다. 이것을 관용적으로하는 방법을 보려면 [Pipes.Group tutorial] (http://hackage.haskell.org/package/pipes-group-1.0.1/docs/Pipes-Group-Tutorial.html)을 공부하고 확인하십시오. [Pipes.Text.lines] (http://hackage.haskell.org/package/pipes-text-0.0.0.15/docs/Pipes-Text.html#v:lines) –

5

, 당신은 그것을 처리하기 위해 Either() Test를 사용할 수 있습니다

parserEither :: Parser (Either() Test) 
parserEither = Right <$> testParser <* endOfLine 
      <|> Left <$> (manyTill anyChar endOfLine *> pure()) 

을이 후에는 Pipes.Prelude에서 제공하는 기능을 사용할 수 있습니다 첫 번째 결과를 제거하는 (그리고 부가 아닌 모든 구문 분석 라인)

producer p = parsed parserEither p 
     >-> P.drop 1 
     >-> P.filter (either (const False) (const True)) 
     >-> P.map (\(Right x) -> x) 

main = IO.withFile testFile IO.ReadMode $ \testHandle -> runEffect $ 
     for (producer (fromHandle testHandle)) (lift . print)