0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Haskell] IO () 型の for ループ

Last updated at Posted at 2023-10-30

Haskell は関数型プログラミング言語のため、他のプログラミング言語における「for ループ」そのものはありません。

ただし、モナドを返す場合はそれに似た書き方が可能です。

1. 再帰

Haskell ではモナドかどうかにかかわらず、再帰でループすることができます。

本記事では深く触れませんが、fix 関数を用いると無名関数で再帰することができます。

import Control.Monad (when)
import Data.Function (fix)

f :: Int -> IO ()
f x = do
    when (x > 1) $ do
        f $ x - 1
    putStrLn $ "f: " ++ show x

main :: IO ()
main = do

    f 3

    fix
        (\ (f' :: Int -> IO ()) (x :: Int) -> do
            when (x > 1) $ do
                f' $ x - 1
            putStrLn $ "fix: " ++ show x
        )
        3

    flip fix 3 $ \ (f' :: Int -> IO ()) (x :: Int) -> do
        when (x > 1) $ do
            f' $ x - 1
        putStrLn $ "flip fix: " ++ show x
実行結果
f: 1
f: 2
f: 3
fix: 1
fix: 2
fix: 3
flip fix: 1
flip fix: 2
flip fix: 3

参考「fix - Data.Function

2. モナドをループさせる関数

モナドをループさせる関数として以下のようなものがあります:

  • traverse_ f xs, for_ xs f
    • xs の各要素 x に関数 f を適用する
  • replicateM_ n x
    • xn 回繰り返す
  • foldM_ f a xs
    • xs の各要素 x に関数 f を適用する
    • 「関数 f の戻り値 pure a'」の a' を次のループ時の「関数 f の引数 a」とする
  • zipWithM_ f xs ys
    • xs の各要素 xys の各要素 y を対にして関数 f を適用する
  • sequenceA_ xs
    • xs の各要素 x を実行する

for_ = flip traverse_ です。
※他に歴史的な理由により残されている関数がありますが、mapM_ = traverse_, forM_ = for_ および sequence_ = sequenceA_ です。

※各関数の型の説明は後述。

参考「traverse_ - Data.Foldable
参考「for_ - Data.Foldable
参考「replicateM_ - Control.Monad
参考「foldM_ - Control.Monad
参考「zipWithM_ - Control.Monad
参考「sequenceA_ - Data.Foldable

2.1. 使い方

IO () 型に関して各関数は以下のように使用します。

import Control.Monad (foldM_, replicateM_, zipWithM_)
import Data.Foldable (for_, sequenceA_, traverse_)

main :: IO ()
main = do

    traverse_
        (\ (x :: Int) -> do
            putStrLn $ "traverse_: " ++ show x
        )
        [1..3]

    for_ [1..3] $ \ (x :: Int) -> do
        putStrLn $ "for_: " ++ show x

    replicateM_ 3 $ do
        putStrLn "replicateM_"

    foldM_
        (\ (a :: Int) (x :: Int) -> do
            putStrLn $ "foldM_: " ++ show x ++ ", " ++ show a
            pure $ a + 1
        )
        4
        [1..3]

    zipWithM_
        (\ (x :: Int) (y :: Int) -> do
            putStrLn $ "zipWithM_: " ++ show x ++ ", " ++ show y
        )
        [1..3]
        [4..6]

    sequenceA_
        [ putStrLn "sequenceA_: 1"
        , putStrLn "sequenceA_: 2"
        , putStrLn "sequenceA_: 3"
        ]
実行結果
traverse_: 1
traverse_: 2
traverse_: 3
for_: 1
for_: 2
for_: 3
replicateM_
replicateM_
replicateM_
foldM_: 1, 4
foldM_: 2, 5
foldM_: 3, 6
zipWithM_: 1, 4
zipWithM_: 2, 5
zipWithM_: 3, 6
sequenceA_: 1
sequenceA_: 2
sequenceA_: 3

2.2. 型

各関数の型は以下のようになっています。

traverse_   :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
for_        :: (Foldable t, Applicative f) => t a -> (a -> f b) -> f ()

replicateM_ :: Applicative m => Int -> m a -> m ()

foldM_      :: (Foldable t, Monad m) => (b -> a -> m b) -> b   -> t a -> m ()
zipWithM_   :: Applicative m         => (a -> b -> m c) -> [a] -> [b] -> m ()

sequenceA_  :: (Foldable t, Applicative f) => t (f a) -> f ()

ここで t = [], f = IO および m = IO とすると、以下のようになります。

traverse_   :: (a -> IO b) -> [a] -> IO ()
for_        :: [a] -> (a -> IO b) -> IO ()

replicateM_ :: Int -> IO a -> IO ()

foldM_      :: (b -> a -> IO b) -> b   -> [a] -> IO ()
zipWithM_   :: (a -> b -> IO c) -> [a] -> [b] -> IO ()

sequenceA_  :: [IO a] -> IO ()

戻り値が不要なところを IO () 型とすると、以下のようになります。

traverse_   :: (a -> IO ()) -> [a] -> IO ()
for_        :: [a] -> (a -> IO ()) -> IO ()

replicateM_ :: Int -> IO () -> IO ()

foldM_      :: (b -> a -> IO b ) -> b   -> [a] -> IO ()
zipWithM_   :: (a -> b -> IO ()) -> [a] -> [b] -> IO ()

sequenceA_  :: [IO ()] -> IO ()
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?