LoginSignup
3
2

More than 5 years have passed since last update.

【解答例】Haskell モナド変換子 超入門

Last updated at Posted at 2015-01-04

Haskell モナド変換子 超入門の解答例です。

Stateモナド

【問1】Stateモナドを扱うreturnStateTを使って、runStaterunStateTを使って再実装してください。

import Control.Monad.Identity
import Control.Monad.State hiding (return, runState)

return' x = StateT $ \s -> Identity (x, s)
runState' st = runIdentity . runStateT st

main = do
    let st = return' 1
    print $ runState' st ()
実行結果
(1,())

Identityモナドが挟まっていることを意識します。

StateTモナド変換子

再実装

【問2】StateTモナド変換子を扱うbind, get, modify, liftを実装してください。doは使わないでください。>>=はStateTモナド変換子以外にのみ使ってください。

import Control.Monad
import Control.Monad.State hiding (get, modify, lift)

a `bind` b = StateT $ \s ->
    runStateT  a    s >>= \(r, s1) ->
    runStateT (b r) s1

get = StateT $ \s -> return (s, s)
modify f = StateT $ \s -> return ((), f s)
lift m = StateT $ \s -> m >>= \a -> return (a, s)

fact x = (`execStateT` 1) $
    forM_ [1..x] $ \i ->
        modify (* i) `bind` \_ ->
        get `bind` \v ->
        lift $ putStrLn $ "*" ++ show i ++ " -> " ++ show v

main = fact 5 >>= print
実行結果
*1 -> 1
*2 -> 2
*3 -> 6
*4 -> 24
*5 -> 120
()

書き直し

【問3】問2のfactdo<-で書き直してください。問2で再実装した関数は使わないでください。

import Control.Monad
import Control.Monad.State

fact x = (`execStateT` 1) $ do
    forM_ [1..x] $ \i -> do
        modify (* i)
        v <- get
        lift $ putStrLn $ "*" ++ show i ++ " -> " ++ show v

main = fact 5 >>= print
実行結果
*1 -> 1
*2 -> 2
*3 -> 6
*4 -> 24
*5 -> 120
()

モナド変換子で合成

Maybeモナド

【問4】次のコードは種類を判別しながら文字列を読み進めることを意図しています。Maybeモナドを使ってエラーが起きないように修正してください。モナド変換子は使わないでください。

import Data.Char

getch f (x:xs) | f x = Just (x, xs)
getch _ _            = Nothing

test s0 = do
    (ch1, s1) <- getch isUpper s0
    (ch2, s2) <- getch isLower s1
    (ch3, s3) <- getch isDigit s2
    return [ch1, ch2, ch3]

main = do
    print $ test "Aa0"
    print $ test "abc"
Just "Aa0"
Nothing

StateTモナド変換子

【問5】問4の解答をStateTモナド変換子を使って書き直してください。

import Data.Char
import Control.Monad.State

getch f = StateT getch where
    getch (x:xs) | f x = Just (x, xs)
    getch _            = Nothing

test = evalStateT $ do
    ch1 <- getch isUpper
    ch2 <- getch isLower
    ch3 <- getch isDigit
    return [ch1, ch2, ch3]

main = do
    print $ test "Aa0"
    print $ test "abc"
Just "Aa0"
Nothing

他のモナド変換子

【問6】次のコードのprintmainから各test*doの中に移動してください。実行結果が同じになるように調整してください。

import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.List

testR x = (`runReaderT` x) $ do
    a <- ask
    lift $ print $ a + 1

testW x = runWriterT $ do
    tell $ show x
    lift $ print $ x + 1

testL x = runListT $ do
    lift $ print [x + 1]

main = do
    testR 0
    testW 0
    testL 0
実行結果
1
1
[1]

モナド変換子の評価結果がIOモナドで返ってくるため、mainの中に直接記述できます。

liftIO

【問7】問6をliftIOで解いてください。

import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.List

testR x = (`runReaderT` x) $ do
    a <- ask
    liftIO $ print $ a + 1

testW x = runWriterT $ do
    tell $ show x
    liftIO $ print $ x + 1

testL x = runListT $ do
    liftIO $ print [x + 1]

main = do
    testR 0
    testW 0
    testL 0
実行結果
1
1
[1]

liftM

【問8】次のリスト内包表記をliftM系の関数で書き直してください。

import Control.Monad

main = do
    print $ liftM2 (,) [0, 1] [0, 2]
    print $ liftM2 (+) [0, 1] [0, 2]
実行結果
[(0,0),(0,2),(1,0),(1,2)]
[0,2,1,3]

(+)だけだと動きが分かりにくいので、(,)と比較してみてください。

3
2
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
3
2