2014-04-06 7 views
2

작은 파일 전체에 Parsec 파서를 실행하려고하는데 열려있는 파일이 너무 많다는 오류가 발생합니다. 나는 엄격한 입출력을 사용해야 함을 알고 있지만 어떻게해야하는지 잘 모릅니다."getCurrentDirectory : 리소스 소모 (열려있는 파일이 너무 많음)"오류

files = getDirectoryContents historyFolder 

hands :: IO [Either ParseError [Hand]] 
hands = join $ sequence <$> parseFromFile (many hand) <<$>> files 

참고 : 내 <<$>> 기능이있다 :이 문제가있는 코드 나는 당신의 parseFromFile 기능에 그것을 포함 (지금처럼 아마 좋은 아이디어를 보이는 것을 모르는

(<<$>>) :: (Functor f1, Functor f2) => (a -> b) -> f1 (f2 a) -> f1 (f2 b) 
a <<$>> b = (a <$>) <$> b 
+0

문제는'parseFromFile'이 너무 게으른 것입니다, 즉 당신이 그것을 포함 할 것이라고, 나는 변경 제안하는 위치이다. 게다가,'pipes' 또는'conduit' 패키지를 사용하는 것은 좋은 생각 일 수 있습니다. – Markus1189

답변

4

질문), 나는 당신이 Prelude.readFile을 사용하고 있다고 추측하고 있는데, @ Markus1189가 게으른 I/O를 포함하고 있다고 지적하고 있습니다. 엄격한 I/O를 얻으려면 Data.Text.IO.readFile과 같이 엄격한 readFile 만 필요합니다.

pipes 또는 conduit과 같은 스트리밍 데이터 라이브러리를 사용하면 전체 파일을 한 번에 읽지 않아도됩니다. 필자의 지식에 따르면 parsec은이를 가능하게하는 스트리밍 인터페이스를 제공하지 않습니다. 반면에 attoparsec은 그러한 스트리밍 인터페이스를 포함하며 파이프와 도관은 모두 attoparsec 어댑터 라이브러리 (예 : Data.Conduit.Attoparsec)를 가지고 있습니다.

TL; DR :

import qualified Data.Text as T 
import qualified Data.Text.IO as TIO 

readFileStrict :: FilePath -> IO String 
readFileStrict = fmap T.unpack . TIO.readFile 
+0

'parseFromFile'은 parsec의 일부이고 [lazy read] (http://hackage.haskell.org/package/parsec- 3.1.5/docs/Text-Parsec-ByteString-Lazy.html)를 ByteString.Lazy의'readFile' 또는 더 엄격한 변형 (http://hackage.haskell.org/package/parsec-3.1. 5/docs/Text-Parsec-ByteString.html)을'bracket openBinaryFile ... hClose'과 함께 사용합니다. – Zeta

0

당신은이 경우 parseFromFile에, 당신의 IO 작업의 엄격함을 적용 할 BangPatterns 언어 확장을 사용할 수 있습니다 : 당신은 아마 다음과 같은 도우미 함수가 필요합니다. 목록에 다음 파일로 이동하기 전에

hands :: [String] → IO [Either ParseError [Hand]] 
hands [] = return [] 
hands (f:fs) = do 
    !res ← parseFromFile hand f 
    others ← hands fs 
    return (res:others) 

parseFromFile의 각 호출의 결과에 손을 대기이 버전의 예를 들어 기능 hands을 변경할 수 있습니다. 일단 이것을 받으면 문제는 사라질 것입니다. 전체 작업 장난감 예는 다음과 같습니다

{-# LANGUAGE BangPatterns #-} 
import Control.Monad 
import Control.Applicative hiding (many) 
import Data.Char (isDigit) 
import System.Directory (getDirectoryContents) 
import System.FilePath ((</>)) 
import Text.ParserCombinators.Parsec 

data Hand = Hand Int deriving Show 

hand :: GenParser Char st [Hand] 
hand = do 
    string "I'm file " 
    num ← many digit 
    newline 
    eof 
    return [Hand $ read num] 

files :: IO [String] 
files = map ("manyfiles" </>) 
     ∘ filter (all isDigit) <$> getDirectoryContents "manyfiles" 

hands :: [String] → IO [Either ParseError [Hand]] 
hands [] = return [] 
hands (f:fs) = do 
    !res ← parseFromFile hand f 
    others ← hands fs 
    return (res:others) 

main :: IO 
main = do 
    results ← files >≥ hands 
    print results