11

我是haskell的新手,我通读并消化了Learn You A Haskell For Great Good,一路上尝试了一些事情。对于我的第一个项目,我想尝试经典:FizzBu​​zz。所以我想出了以下代码:

import System.IO

fizzBuzz :: (Integral a) => a -> String
fizzBuzz num
    | fizz && buzz = "FizzBuzz"
    | fizz = "Fizz"
    | buzz = "Buzz"
    | otherwise = show num
    where fizz = num `mod` 3 == 0
          buzz = num `mod` 5 == 0

main = print $ map fizzBuzz [1..100]

工作得很好,除了我有一个看起来相当密集的列表,很难阅读。所以我尝试了这个主要功能:

main = map putStrLn $ map fizzBuzz [1..100]

这给了我错误Couldn't match expected type 'IO t' against inferred type '[IO ()]'。我尝试了六种方法,但似乎都没有帮助。做我想做的事情的正确方法是什么?

4

1 回答 1

26
map :: (a -> b) -> [a] -> [b]
putStrLn :: Show a => a -> IO ()
map putStrLn :: Show a => [a] -> [IO ()]

你有一个IO ()动作列表。

main :: IO ()

您需要将它们加入到单个IO ()操作中。

您要做的是按顺序/ sequence_IO ()执行这些操作中的每一个:

sequence :: Monad m => [m a] -> m [a]
sequence_ :: Monad m => [m a] -> m ()

为方便起见,mapM / mapM_将在列表上映射一个函数并对生成的一元结果进行排序。

mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()

所以你的固定代码看起来像这样:

main = mapM_ putStrLn $ map fizzBuzz [1..100]

虽然我可能会这样写:

main = mapM_ (putStrLn . fizzBuzz) [1..100]

甚至这样:

main = putStr $ unlines $ map fizzBuzz [1..100]

让我们编写自己的sequence. 我们想让它做什么?

sequence [] = return []
sequence (m:ms) = do
    x <- m
    xs <- sequence ms
    return $ x:xs
  • 如果列表中没有任何内容,则返回(注入 monad)一个空的结果列表。
  • 否则,在单子中,
    • 绑定(对于IOmonad,这意味着执行)第一个结果。
    • sequence列表的其余部分;绑定该结果列表。
    • 返回第一个结果的 cons 和其他结果的列表。

GHC 的库使用类似的东西,foldr (liftM2 (:)) (return [])但对新手来说很难解释;现在,相信我的话,它们是等价的。

sequence_更容易,因为它不需要跟踪结果。GHC 的库将其实现为sequence_ ms = foldr (>>) (return ()) ms. 让我们扩展 的定义foldr

  sequence [a, b, c, d]
= foldr (>>) (return ()) [a, b, c, d]
= a >> (b >> (c >> (d >> return ())))

换句话说,“做a,丢弃结果;做b;丢弃结果,……最后,返回()”。

mapM  f xs = sequence  $ map f xs
mapM_ f xs = sequence_ $ map f xs

另一方面,使用替代unlines解决方案,您甚至根本不需要了解 monad。

做什么unlines?嗯,lines "a\nb\nc\nd\n" = ["a", "b", "c", "d"]当然unlines ["a", "b", "c", "d"] = "a\nb\nc\nd\n"

unlines $ map fizzBuzz [1..100]= unlines ["1", "2", "Fizz", ..]="1\n2\nFizz\n..."然后它转到putStr. 多亏了 Haskell 懒惰的魔力,完整的字符串永远不需要在内存中构造,所以这会很高兴地达到[1..1000000]或更高:)

于 2010-01-06T21:53:16.050 回答