2010-10-20 2 views
4

Я новичок, и монады меня полностью смущают. Учитывая список имен файлов, я хотел бы знать, существуют ли все файлы.Определите, существует ли список файлов в Haskell

Вообще, я хотел бы сделать:

import System.Directory 
allFilesPresent files = foldr (&&) True (map doesFileExist files) 

Однако я не знаю, что это правильный способ сделать это, потому что есть IO Bool вместо Bool участие здесь.

Помочь с и объяснением было бы очень приятно, спасибо!

+5

Следует отметить, что 'foldr (&&) true' функция Prelude.and. – HaskellElephant

+1

Я настоятельно рекомендую прочитать http://learnyouahaskell.com/input-and-output. Затем вы можете прочитать некоторые другие главы о функциях, аппликациях, моноидах и монадах. Очень нежное и постепенное введение в эти темы. – Daniel

ответ

10

Вы правы, ваш код не работает, потому что map doesFileExist files возвращает список IO Bool s вместо Bool. Чтобы исправить это, вы можете использовать mapM вместо map, который даст вам IO [Bool]. Вы можете распаковать, используя >>= или <- внутри do -block, а затем используйте foldr (&&) на распакованных [Bool] и return, что. Результатом будет 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 
+1

Не забывайте 'foldr (&&) True == и' (или достаточно близко для этого варианта использования) –

+1

Или, если вам удобно устанавливать библиотеки Haskell, библиотека монад-петель имеет функцию 'allM :: Monad m = > (a -> m Bool) -> [a] -> m Bool' в модуле 'Control.Monad.Loops'. С этой функцией 'allFilesPresent = allM doesFileExist' – mokus

6

doesFileExist "foo.txt" - IO Bool, что означает, что его результат зависит от состояния внешнего мира.

Вы находитесь на правильном пути с map doesFileExist files - это выражение вернет [IO Bool] или список выражений, зависящих от мира. Фактически необходимо выражение IO, содержащее список bools. Вы можете получить это с помощью 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 
+0

Я думаю, вы имеете в виду' allFilesPresent = fmap и. mapM doesFileExist' – HaskellElephant

+0

Неверно сказать, что 'IO T' является' T', который зависит от мира. Это * рецепт * для получения 'T', который может выполняться несколько раз и каждый раз производить разные результаты. Рецепты и пирожные - это совершенно разные вещи. – keegan

+0

@keegan: согласился, и чтобы уточнить эту метафору, конечный продукт _executing_ рецепта торта - это то, что я бы назвал пирожным, который зависит от предыдущего состояния мира. Из любого рецепта (IO Cake) можно создать много таких тортов. – mokus

5

Обратите внимание, что если вы используете 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 
Смежные вопросы