我是一個新手,monads讓我完全困惑。給出一個文件名列表,我想知道是否所有文件都存在。確定一個文件列表是否存在於Haskell
一般情況下,我想這樣做:
import System.Directory
allFilesPresent files = foldr (&&) True (map doesFileExist files)
但是我不知道什麼是應該做的正確方法,因爲有IO Bool
,而不是這裏涉及Bool
。
幫助和解釋將是非常好的,謝謝!
我是一個新手,monads讓我完全困惑。給出一個文件名列表,我想知道是否所有文件都存在。確定一個文件列表是否存在於Haskell
一般情況下,我想這樣做:
import System.Directory
allFilesPresent files = foldr (&&) True (map doesFileExist files)
但是我不知道什麼是應該做的正確方法,因爲有IO Bool
,而不是這裏涉及Bool
。
幫助和解釋將是非常好的,謝謝!
你是對的,你的代碼不起作用,因爲map doesFileExist files
返回的是IO Bool
的列表,而不是Bool
。要解決這個問題,你可以使用mapM
而不是map
,這會給你一個IO [Bool]
。您可以使用>>=
或<-
在do
-block內解壓,然後在解壓後的[Bool]
和return
上使用foldr (&&)
即可。結果將是IO Bool
。就像這樣:
import System.Directory
allFilesPresent files = mapM doesFileExist files >>=
return . foldr (&&) True
或者用做記號:
import System.Directory
allFilesPresent files = do bools <- mapM doesFileExist files
return $ foldr (&&) True bools
或者使用liftM
從Control.Monad
:
allFilesPresent files = liftM (foldr (&&) True) $ mapM doesFileExist files
或者使用<$>
從Control.Applicative
:
allFilesPresent files = foldr (&&) True <$> mapM doesFileExist files
不要忘了'foldr(&&)True ==和'(或者對於這個用例來說已經夠用了) – 2010-10-20 22:35:26
或者,如果你願意安裝Haskell庫,monad-loops庫有一個函數'allM :: Monad m = >(a - > m Bool) - > [a] - > m Bool'在Control.Monad.Loops模塊中。有了這個函數,'allFilesPresent = allM doesFileExist' – mokus 2010-10-21 13:37:46
doesFileExist "foo.txt"
是IO Bool
,這意味着它的結果取決於外界的狀態。
你在正確的軌道上map doesFileExist files
- 此表達式將返回[IO Bool]
或世界相關表達式的列表。實際需要的是包含一個bool列表的IO
表達式。您可以使用sequence
得到這樣的:
sequence :: Monad m => [m a] -> m [a]
,或者因爲你僅僅使用序列/圖,mapM
輔助函數:
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f xs = sequence (map f xs)
讓我們回到你的代碼。下面是使用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)
的替代版本使用更多的聲明語法;這可能是一個經驗豐富的Haskell程序員會寫什麼:
allFilesPresent :: [String] -> IO Bool
allFilesPresent = fmap and . mapM doesFileExist
我想你的意思是'allFilesPresent = fmap和。 mapM doesFileExist' – HaskellElephant 2010-10-20 22:13:32
說'IO T'是一個取決於世界的'T'是不準確的。這是一個獲得'T'的配方*,可以多次執行,每次產生不同的結果。食譜和蛋糕是完全不同的東西。 – keegan 2010-10-21 07:13:02
@keegan:同意了,並且詳細說明這個隱喻,_executing_蛋糕配方的最終產品是我稱之爲蛋糕的事物,它取決於世界的先前狀態。從任何給定的食譜(IO蛋糕)可以創建許多這樣的蛋糕。 – mokus 2010-10-21 13:40:20
請注意,如果您使用sequence
或mapM
,你是不是選擇短路;即使其中一個文件不存在,您仍然檢查其餘文件是否存在。如果你想短路了以下工作:
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
或等效採用monad-loops package:
import System.Directory
import Control.Monad.Loops
allFilesPresent :: [FilePath] -> IO Bool
allFilesPresent = allM doesFileExist
它應該指出的是'foldr相似(&&)TRUE'是功能Prelude.and。 – HaskellElephant 2010-10-20 22:02:58
我強烈建議閱讀http://learnyouahaskell.com/input-and-output然後您可以閱讀Functors,Applicative,Monoid和Monads的其他章節。非常溫和和逐步介紹這些主題。 – Daniel 2010-10-20 22:17:46