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 ()それらを 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]以上になります :)

于 2010-01-06T21:53:16.050 に答える