【问题标题】:Haskell: how to make a list of files and a list of directories out of one common listHaskell:如何从一个通用列表中制作文件列表和目录列表
【发布时间】:2018-05-25 02:26:23
【问题描述】:

这是一个新手问题。假设我想将文件和目录列表分离为文件列表和目录列表:

getFilesAndDirs :: [FilePath] -> ([FilePath], [FilePath])
getFilesAndDirs paths =
  let ...
  in (dirs, files)

可能这是一个无可救药的重复,我只是错过了正确的关键字。 这样做(和调用)这个的正确方法是什么?

文件和目录随机出现。

【问题讨论】:

  • Data.List 中有一个名为 partition 的函数,看起来就像您要求的那样。
  • @MarkSeemann:我认为这里的一个问题是谓词是一元的(FilePath -> IO Bool
  • n.m.是的:没有办法知道路径是指文件还是目录,除非实际访问该路径,这是一个IO 操作。也就是说,除非您对启发式方法(例如是否存在标准文件扩展名)感到满意,但这通常不会很可靠。
  • partitionM :: Monad m => (a -> m Bool) -> [a] -> m ([a], [a]) from extra's Control.Monad.Extra 似乎是票。

标签: list file haskell partition io-monad


【解决方案1】:

Data.List 包具有 partition :: (a -> Bool) -> [a] -> ([a],[a]) 函数,该函数根据谓词将 as 列表拆分为两个 as 列表的元组。

然而问题是当我们检查一个文件是否是一个目录时,我们可能会使用isDirectory :: FilePath -> IO Bool所以我们不能直接使用它作为谓词(因为IO Bool不等于Bool)。

不过,我们可以编写自己的partitionM,并使用那个:

import Data.Bool(bool)
import Data.Foldable(foldrM)

partitionM :: (Foldable t, Monad m) => (a -> m Bool) -> t a -> m ([a], [a])
partitionM p = foldrM (selectM p) ([],[])

selectM :: Monad m => (a -> m Bool) -> a -> ([a], [a]) -> m ([a], [a])
selectM p x (ts,fs) = p x >>= return . bool (ts, x:fs) (x:ts,fs)

然后我们可以像这样使用它:

import System.Directory(isDirectory)

getFilesAndDirs :: [FilePath] -> IO ([FilePath], [FilePath])
getFilesAndDirs = partitionM isDirectory

注意它是IO ([FilePath], [FilePath]),因为我们需要执行 I/O 来检查路径是否确实是目录(而不是文件)。

【讨论】:

  • 你是不是错过了selectM结果类型的monad?另外,惰性模式匹配值得吗?
  • @SebastianRedl:谢谢。这确实可能不值得:)
  • 虽然这是风格问题,但我更喜欢使用do 表示法并“从IO 中提取值”,然后将它们传递给纯函数。最终结果是相同的,但请参阅我的答案以获得替代方案。
【解决方案2】:
module SeparateFiles where
    import Data.String
    import System.FilePath.Posix


    type Path = FilePath
    getFilesAndDirs :: Path -> [(Path,Path)]
    getFilesAndDirs path = [splitFileName path]

我了解到您希望将 FilePath 提取拆分为文件和目录。我为您提供了一个非常简短的示例。

【讨论】:

    【解决方案3】:

    您可以使用do 表示法来编排程序的不纯部分,然后使用像partition 这样的内置(纯)函数来完成实际工作。这是一个例子:

    module Q47755054 (getFilesAndDirs) where
    
    import Data.List (partition)
    import Data.Bifunctor (bimap)
    import System.Directory (doesDirectoryExist)
    
    tagPath :: FilePath -> IO (FilePath, Bool)
    tagPath path = do
      isDirectory <- doesDirectoryExist path
      return (path, isDirectory)
    
    getFilesAndDirs :: [FilePath] -> IO ([FilePath], [FilePath])
    getFilesAndDirs paths = do
      taggedPaths <- mapM tagPath paths
      return $ bimap (fmap fst) (fmap fst) $ partition snd taggedPaths
    

    请注意,这使用内置的 mapM 函数来获取不纯的值列表 (IO [(FilePath, Bool)]),但由于 do 语法和 &lt;- 绑定,taggedPaths 看起来像一个纯值 ([(FilePath, Bool)]),因此您可以将其传递给 partition

    另外请注意,tagPath 只是一个模块级别的辅助函数,不会由模块导出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多