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]
以上になります :)