确定Haskell中是否存在文件列表

Dra*_*sha 5 monads haskell

我是新手,monads让我完全糊涂了.给定一个文件名列表,我想知道是否所有文件都存在.

一般来说,我想做:

import System.Directory
allFilesPresent files = foldr (&&) True (map doesFileExist files)
Run Code Online (Sandbox Code Playgroud)

但是我不知道这样做的正确方法是什么,因为这里IO Bool没有Bool涉及到.

帮助和解释会非常好,谢谢!

sep*_*p2k 11

你是对的,你的代码不起作用,因为map doesFileExist files返回的是IO Bools 的列表而不是Bool.要解决这个问题,你可以使用mapM而不是map,这将给你一个IO [Bool].您可以解压,使用>>=<-一个内部do-阻塞,然后用foldr (&&)在解压缩[Bool]return那个.结果将是一个IO Bool.像这样:

import System.Directory
allFilesPresent files = mapM doesFileExist files >>=
                        return . foldr (&&) True
Run Code Online (Sandbox Code Playgroud)

或使用做法:

import System.Directory
allFilesPresent files = do bools <- mapM doesFileExist files
                           return $ foldr (&&) True bools
Run Code Online (Sandbox Code Playgroud)

或者使用liftM来自Control.Monad:

allFilesPresent files = liftM (foldr (&&) True) $ mapM doesFileExist files
Run Code Online (Sandbox Code Playgroud)

或者使用<$>来自Control.Applicative:

allFilesPresent files = foldr (&&) True <$> mapM doesFileExist files
Run Code Online (Sandbox Code Playgroud)


Joh*_*kin 6

doesFileExist "foo.txt"是一个IO Bool,这意味着它的结果取决于外部世界的状态.

你正走在正确的轨道上map doesFileExist files- 这个表达式会返回[IO Bool],或者依赖于世界的表达式列表.实际需要的是一个IO包含bool列表的表达式.你可以使用sequence:

sequence :: Monad m => [m a] -> m [a]
Run Code Online (Sandbox Code Playgroud)

或者,因为您只是使用序列/映射,mapM辅助函数:

mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f xs = sequence (map f xs)
Run Code Online (Sandbox Code Playgroud)

让我们回到你的代码.这是一个使用的版本mapM,带有注释:

import System.Directory

-- When figuring out some unfamiliar libraries, I like to use type annotations
-- on all top-level definitions; this will help you think through how the types
-- match up, and catch errors faster.
allFilesPresent :: [String] -> IO Bool

-- Because allFilesPresent returns a computation, we can use do-notation to write
-- in a more imperative (vs declarative) style. This is sometimes easier for students
-- new to Haskell to understand.
allFilesPresent files = do

    -- Run 'doesFileExist' tests in sequence, storing the results in the 'filesPresent'
    -- variable. 'filesPresent' is of type [Bool]
    filesPresent <- mapM doesFileExist files

    -- The computation is complete; we can use the standard 'and' function to join the
    -- list of bools into a single value.
    return (and filesPresent)
Run Code Online (Sandbox Code Playgroud)

替代版本使用更多声明性语法; 这可能是一位经验丰富的Haskell程序员所写的:

allFilesPresent :: [String] -> IO Bool
allFilesPresent = fmap and . mapM doesFileExist
Run Code Online (Sandbox Code Playgroud)


Tsu*_*Ito 5

请注意,如果您使用sequencemapM,您选择不短路; 即使其中一个文件不存在,您仍然检查其余文件是否存在.如果您想要短路,以下工作:

import System.Directory

andM :: Monad m => [m Bool] -> m Bool
andM [] =
    return True
andM (m : ms) = do
    b <- m
    if b then
      andM ms
     else
      return False

allFilesPresent :: [FilePath] -> IO Bool
allFilesPresent files = andM $ map doesFileExist files
Run Code Online (Sandbox Code Playgroud)

或者通过使用monad-loops包等效:

import System.Directory
import Control.Monad.Loops

allFilesPresent :: [FilePath] -> IO Bool
allFilesPresent = allM doesFileExist
Run Code Online (Sandbox Code Playgroud)