На прошлой неделе пользователь Masse задал вопрос о рекурсивном перечислении файлов в каталоге в Haskell. Моя первая мысль заключалась в том, чтобы попытаться использовать монадические списки из List
package, чтобы избежать создания всего списка в памяти до начала печати. Я реализовал это следующим образом:
module Main where
import Prelude hiding (filter)
import Control.Applicative ((<$>))
import Control.Monad (join)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.ListT (ListT)
import Data.List.Class (cons, execute, filter, fromList, mapL)
import System (getArgs)
import System.Directory (getDirectoryContents, doesDirectoryExist)
import System.FilePath ((</>))
main = execute . mapL putStrLn . listFiles =<< head <$> getArgs
listFiles :: FilePath -> ListT IO FilePath
listFiles path = liftIO (doesDirectoryExist path) >>= listIfDir
where
valid "." = False
valid ".." = False
valid _ = True
listIfDir False = return path
listIfDir True
= cons path
$ join
$ listFiles
<$> (path </>)
<$> (filter valid =<< fromList <$> liftIO (getDirectoryContents path))
Это прекрасно работает, когда он начинает печатать сразу и использует очень мало памяти. К сожалению, он также в десятки раз медленнее, чем сопоставимая версия FilePath -> IO [FilePath]
.
Что я делаю неправильно? Я никогда не использовал List
package ListT
за пределами таких игрушечных примеров, поэтому я не знаю, какую производительность ожидать, но 30 секунд (против доли секунды) для обработки каталога с помощью ~ 40 000 файлов кажется слишком медленными.