haskell - 使用 Pipes 递归列出目录下的所有文件

标签 haskell recursion directory haskell-pipes

我读完了Pipes tutorial ,我想编写一个函数来递归地列出目录中的所有文件。我尝试使用以下代码:

enumFiles :: FilePath -> Producer' FilePath (PS.SafeT IO) ()
enumFiles path =
  PS.bracket (openDirStream path) (closeDirStream) loop
  where
    loop :: DirStream -> Producer' FilePath (PS.SafeT IO) ()
    loop ds = PS.liftBase (readDirStream ds) >>= checkName
      where
        checkName :: FilePath -> Producer' FilePath (PS.SafeT IO) ()
        checkName ""   = return ()
        checkName "."  = loop ds
        checkName ".." = loop ds
        checkName name = PS.liftBase (getSymbolicLinkStatus newPath)
                         >>= checkStat newPath
          where newPath = path </> name

        checkStat path stat
          | isRegularFile stat = yield path >> loop ds
          | isDirectory stat = enumFiles path
          | otherwise = loop ds

但是,一旦到达return (),该生产者就会终止。我想我没有以正确的方式编写它,但我不知道什么是正确的方法。

最佳答案

只需更改此行:

| isDirectory stat = enumFiles path

| isDirectory stat = enumFiles path >> loop ds

在这种递归情况下,代码缺少递归。

您还可以将此生产者分解为更小的生产者和管道的组合:

{-# LANGUAGE RankNTypes #-}

module Main where

import qualified Pipes.Prelude as P
import qualified Pipes.Safe as PS

import           Control.Monad
import           Pipes
import           System.FilePath.Posix
import           System.Posix.Directory
import           System.Posix.Files

readDirStream' :: FilePath -> Producer' FilePath (PS.SafeT IO) ()
readDirStream' dirpath =
  PS.bracket (openDirStream dirpath) closeDirStream (forever . loop)
  where
    loop stream =
      liftIO (readDirStream stream) >>= yield

enumFiles :: FilePath -> Producer' FilePath (PS.SafeT IO) ()
enumFiles path =
  readDirStream' path
    >-> P.takeWhile (/= "")
    >-> P.filter (not . flip elem [".", ".."])
    >-> P.map (path </>)
    >-> forever (do
                    entry <- await
                    status <- liftIO $ getSymbolicLinkStatus entry
                    when (isDirectory status) (enumFiles entry)
                    when (isRegularFile status) (yield entry))

main :: IO ()
main =
  PS.runSafeT $ runEffect (enumFiles "/tmp" >-> P.stdoutLn)

我发现使用 Control.Monad 中的 foreverPipe.Prelude 中的组合器之一代替手动递归通常很有帮助;它有助于减少像这样的小错别字。然而,正如 children 所说,您的里程可能会有所不同。

关于haskell - 使用 Pipes 递归列出目录下的所有文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44267928/

相关文章:

java - 将单词逐个字符递归地添加到 LinkedHashMap

haskell - 为什么我在 Functor 实例中遇到 GHC 无法匹配类型错误?

c - 这些是等价的吗?

haskell - MonadBaseControl IO ... StateT 实现

java - 如何在Java中递归计算二维数组列

python - 帮助解决读取文件时出现 IOError 的问题

go - 在go中复制一个文件夹

gradle 获取资源默认输出目录

haskell - `forM_` 是惯用的 Haskell 吗?

haskell - 惰性评估是如何以不需要比所产生的 yield 更多的开销的方式实现的?