Предотвращение ошибки "getCurrentDirectory: ресурс исчерпан (слишком много открытых файлов)"

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

files = getDirectoryContents historyFolder

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

Примечание: мой <<$>> функция это:

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

2 ответа

Я не знаю, что ты parseFromFile функция выглядит как сейчас (вероятно, это хорошая идея, чтобы включить это в вопрос), но я предполагаю, что вы используете Prelude.readFile, который, как указывает @Markus1189, включает в себя ленивый ввод / вывод. Чтобы получить строгий ввод / вывод, вам просто нужен строгий readFile, например, Data.Text.IO.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

Вы можете использовать расширение языка BangPatterns для обеспечения строгости ваших операций ввода-вывода, в этом случае parseFromFile, Например, функция hands можно изменить в:

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

Эта версия рук ждет результатов каждого звонка parseFromFile прежде чем перейти к следующему файлу в списке. Как только вы это сделаете, проблема должна исчезнуть. Пример полной рабочей игрушки:

{-# 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
Другие вопросы по тегам