Ral*_*lph 11 haskell directory-structure lazy-evaluation
我正在尝试使用Haskell进行目录结构的递归下降.我想只根据需要检索子目录和文件(懒惰).
我编写了以下代码,但是当我运行它时,跟踪显示在第一个文件之前访问了所有目录:
module Main where
import Control.Monad ( forM, forM_, liftM )
import Debug.Trace ( trace )
import System.Directory ( doesDirectoryExist, getDirectoryContents )
import System.Environment ( getArgs )
import System.FilePath ( (</>) )
-- From Real World Haskell, p. 214
getRecursiveContents :: FilePath -> IO [FilePath]
getRecursiveContents topPath = do
names <- getDirectoryContents topPath
let
properNames =
filter (`notElem` [".", ".."]) $
trace ("Processing " ++ topPath) names
paths <- forM properNames $ \name -> do
let path = topPath </> name
isDirectory <- doesDirectoryExist path
if isDirectory
then getRecursiveContents path
else return [path]
return (concat paths)
main :: IO ()
main = do
[path] <- getArgs
files <- getRecursiveContents path
forM_ files $ \file -> putStrLn $ "Found file " ++ file
Run Code Online (Sandbox Code Playgroud)
如何将文件处理与下降交错?是,这个问题files <- getRecursiveContents path的行动得到了如下之前执行forM_的main?
这正是iteratees/coroutines旨在解决的问题.
您可以轻松地执行此操作pipes.我对你所做的唯一变化getRecursiveContents是,使之成为Producer的FilePathS和对respond返回它的文件名来代替.这使得下游可以立即处理文件名,而不是等待getRecursiveContents完成.
module Main where
import Control.Monad ( forM_, liftM )
import Control.Proxy
import System.Directory ( doesDirectoryExist, getDirectoryContents )
import System.Environment ( getArgs )
import System.FilePath ( (</>) )
getRecursiveContents :: (Proxy p) => FilePath -> () -> Producer p FilePath IO ()
getRecursiveContents topPath () = runIdentityP $ do
names <- lift $ getDirectoryContents topPath
let properNames = filter (`notElem` [".", ".."]) names
forM_ properNames $ \name -> do
let path = topPath </> name
isDirectory <- lift $ doesDirectoryExist path
if isDirectory
then getRecursiveContents path ()
else respond path
main :: IO ()
main = do
[path] <- getArgs
runProxy $
getRecursiveContents path
>-> useD (\file -> putStrLn $ "Found file " ++ file)
Run Code Online (Sandbox Code Playgroud)
这会在遍历树时立即打印出每个文件,并且不需要延迟IO.使用文件名更改操作也非常容易,因为您只需useD使用实际的文件处理逻辑切换出舞台.
要了解更多信息pipes,我强烈建议您阅读Control.Proxy.Tutorial.
使用懒惰IO/unsafe...是不是去的好办法.懒惰IO导致许多问题,包括未封闭的资源和在纯代码中执行不纯的操作.(另请参阅Haskell Wiki上的惰性I/O问题.)
一种安全的方法是使用一些iteratee/enumerator库.(替换有问题的懒惰IO是开发这些概念的动机.)您getRecursiveContents将成为数据源(AKA枚举器).并且一些迭代器将使用这些数据.(另请参阅Haskell wiki上的Enumerator和iteratee.)
枚举器库上有一个教程,它给出了遍历和过滤目录树的示例,实现了一个简单的查找实用程序.它实现了方法
enumDir :: FilePath -> Enumerator FilePath IO b
Run Code Online (Sandbox Code Playgroud)
这基本上就是你需要的.我相信你会发现它很有趣.
也有一个很好的文章,解释在iteratees 单子读者,第16:Iteratee:教老折新把戏由约翰·W·拉托时,笔者iteratee库.
今天,许多人更喜欢新的图书馆,如管道.您可能对比较感兴趣:Enumerators vs. Conduits vs. Pipes的优缺点是什么?.