Предотвращение ошибки "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