map :: (a -> b) -> [a] -> [b]
putStrLn :: Show a => a -> IO ()
map putStrLn :: Show a => [a] -> [IO ()]
IO ()アクションのリストがあります。
main :: IO ()
IO ()それらを 1 つのアクションに結合する必要があります。
あなたがしたいことは、これらの各IO ()アクションをシーケンス/ sequence_で実行することです:
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
- リストに何も残っていない場合は、結果の空のリストを返します (モナドに注入します)。
- そうでなければ、モナド内で、
- 最初の結果をバインドします (
IOモナドの場合、これは実行を意味します)。
sequenceリストの残り。その結果のリストをバインドします。
- 最初の結果のコンスと他の結果のリストを返します。
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 ())))
つまり、「do a、結果を破棄する; do b; 結果を破棄する … 最後に、返す()」.
mapM f xs = sequence $ map f xs
mapM_ f xs = sequence_ $ map f xs
一方、代替unlinesソリューションでは、モナドを知る必要さえありません。
何をし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 の怠惰の魔法のおかげで、完全な文字列をメモリ内で構築する必要はまったくないため、これは喜んで 500[1..1000000]以上になります :)