Workshop/StartHaskell2/exercise14
『もうちょっとだけモナド』 練習問題
Writerモナドを使ってみよう
逆ポーランド記法で書かれた式を計算する関数 solveRPN (p.214) に、Writerモナドを使って計算経過ログをとる機能を追加してみよう。
import Control.Monad.Writer
solveRPN :: String -> Double
solveRPN = head . foldl foldingFunction [] . words
where
foldingFunction (x:y:ys) "*" = (y * x) : ys
foldingFunction (x:y:ys) "+" = (y + x) : ys
foldingFunction (x:y:ys) "-" = (y - x) : ys
foldingFunction xs str = read str : xs
差分リスト
誰かよろしくお願いします
モナドとしての関数
関数のモナドの定義を見ながら、無理やり使える例を探してみよう。
たとえば、リストの前半分だけ取る関数 takeHalf は、こんな風に書ける。(あまり書こうとは思わないけど)
Readerモナドを使ってみよう
いまUnixの環境変数を、変数名と値の連想リストで表すことにする。
type Env = [(String, String)]
environment :: Env
environment =
[ ("HOME", "/home/haskell")
, ("PATH", "/bin:/usr/bin")
, ("LANG", "ja_JP.UTF-8")
]
Control.Monad.Readerで定義されている Reader モナドでは、 ask 関数を使って、環境を参照することができる。
環境変数から指定された変数名の値を探してくる関数 lookupEnv を実装しよう。ただし、変数名が見つからない場合は、空文字を返す。
import Control.Monad.Reader
lookupEnv :: String -> Reader Env String
lookupEnv key = do
env <- ask
...
test = runReader (lookupEnv "LANG") environment
また、$HOME が設定されていればその値を返し、なければ “/etc” を返す関数 getConfPath を書いてみよう。
Stateモナドを使ってみよう
2次元平面上を移動する処理を書いてみよう。(上下左右の座標実装はお任せ)
import Control.Monad.State
type Point = (Int, Int)
move :: Char -> State Point ()
move 'u' = -- 上へ移動
move 'd' = -- 下へ移動
move 'l' = -- 左へ移動
move 'r' = -- 右へ移動
moveGame :: String -> State Point ()
moveGame [] =
moveGame (c:cs) =
main :: IO ()
main = print $ evalState (moveGame "ulrdddlr") (0,0)
スタックを拡張しよう
(p.333) で出てきたスタックに、以下の操作を追加しよう。
- undo : 直前の操作を取り消し、状態を元に戻す
- clear : スタックを空にする。ただし操作履歴の情報は残る(undoできるようにしておく)
type Stack = -- 新しいスタックの定義
pop :: Stack -> State Stack Int
push :: Int -> Stack -> State Stack ()
undo :: Stack -> State Stack ()
clear :: Stack -> State Stack ()
実行例
ghci> runState (push 1 >> clear) [0]
((), [])
ghci> runState (push 1 >> clear >> undo) [0]
((), [1, 0])
ghci> runState (pop >> undo) [0]
((), [0])
Eitherモナド
(p.342 NOTE) ピエールのバランス棒に鳥がとまる様子をシミュレートしました。ピエールが滑って落ちた瞬間に棒の左右に何羽の鳥が止まってるか分かるように、Eitherモナドを使って書き直してみよう。
import Control.Monad.Error
type Birds = Int
type Pole = (Birds, Birds)
landLeft :: Birds -> Pole -> Maybe Pole
landLeft n (left, right)
| abs ((left + n) - right) < 4 = Just (left + n, right)
| otherwise = Nothing
landRight :: Birds -> Pole -> Maybe Pole
landRight n (left, right)
| abs (left - (right + n)) < 4 = Just (left, right + n)
| otherwise = Nothing
banana :: Pole -> Maybe Pole
banana _ = Nothing
routine :: Maybe Pole
routine = do
start <- return (0,0)
first <- landLeft 2 start
second <- landRight 2 first
landLeft 1 second
Prob(確率)モナド
(p.361) 結果が一致する事象の確率をまとめる関数 joinSameOutcomes を書いてみよう。
import Data.Ratio
newtype Prob a = Prob { getProb :: [(a, Rational)] } deriving Show
instance Functor Prob where
fmap f (Prob xs) Prob $ map (\(x, p) -> (f x, p)) xs
instance Monad Prob where
return x = Prob [(x, 1%1)]
m >>= f = flatten (fmap f m)
fail _ = Prob []
flatten :: Prob (Prob a) -> Prob a
flatten (Prob xs) = Prob $ concat $ map multAll xs
where multAll (Prob innerxs, p) = map (\(x, r) -> (x, p*r)) innerxs
joinSameOutcomes :: Eq a => Prob a -> Prob a
joinSameOutcomes = ....