Haskell: 如何从一个公共列表中创建文件列表和目录列表
Haskell: how to make a list of files and a list of directories out of one common list
这是一个新手问题。假设我想把一个文件和目录列表分成一个文件列表和一个目录列表:
getFilesAndDirs :: [FilePath] -> ([FilePath], [FilePath])
getFilesAndDirs paths =
let ...
in (dirs, files)
可能这是无可救药的重复,我只是错过了正确的关键字。
这样做(和调用)的正确方法是什么?
文件和目录随机出现。
Data.List
包具有 partition :: (a -> Bool) -> [a] -> ([a],[a])
函数,它根据谓词将 a
列表拆分为两个 a
列表的元组。
然而问题是当我们检查一个文件是否是一个目录时,我们可能会使用 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] -> <b>IO</b> ([FilePath], [FilePath])
getFilesAndDirs = partitionM isDirectory
请注意,这是一个 IO ([FilePath], [FilePath])
,因为我们需要执行 I/O 来检查路径是否确实是目录(而不是文件)。
module SeparateFiles where
import Data.String
import System.FilePath.Posix
type Path = FilePath
getFilesAndDirs :: Path -> [(Path,Path)]
getFilesAndDirs path = [splitFileName path]
我了解到您想将提取的 FilePath 拆分为文件和目录。我为您提供了一个非常简短的示例。
您可以使用 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
语法和 <-
绑定,taggedPaths
'looks' 就像一个纯值 ([(FilePath, Bool)]
),因此你可以将它传递给 partition
.
另外请注意,tagPath
只是一个模块级辅助函数,不会由模块导出。
这是一个新手问题。假设我想把一个文件和目录列表分成一个文件列表和一个目录列表:
getFilesAndDirs :: [FilePath] -> ([FilePath], [FilePath])
getFilesAndDirs paths =
let ...
in (dirs, files)
可能这是无可救药的重复,我只是错过了正确的关键字。 这样做(和调用)的正确方法是什么?
文件和目录随机出现。
Data.List
包具有 partition :: (a -> Bool) -> [a] -> ([a],[a])
函数,它根据谓词将 a
列表拆分为两个 a
列表的元组。
然而问题是当我们检查一个文件是否是一个目录时,我们可能会使用 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] -> <b>IO</b> ([FilePath], [FilePath])
getFilesAndDirs = partitionM isDirectory
请注意,这是一个 IO ([FilePath], [FilePath])
,因为我们需要执行 I/O 来检查路径是否确实是目录(而不是文件)。
module SeparateFiles where
import Data.String
import System.FilePath.Posix
type Path = FilePath
getFilesAndDirs :: Path -> [(Path,Path)]
getFilesAndDirs path = [splitFileName path]
我了解到您想将提取的 FilePath 拆分为文件和目录。我为您提供了一个非常简短的示例。
您可以使用 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
语法和 <-
绑定,taggedPaths
'looks' 就像一个纯值 ([(FilePath, Bool)]
),因此你可以将它传递给 partition
.
另外请注意,tagPath
只是一个模块级辅助函数,不会由模块导出。