fugafuga.write

日々のログ

すごいH本 part88

do 記法とリスト内包表記

リスト内包表記はリストモナドの構文糖衣である。

listOfTuples :: [(Int, Char)]
listOfTuples = do
    n <- [1,2]
    ch <- ['a','b']
    return (n, ch)

*Main> [(n, ch) | n <- [1,2], ch <- ['a','b']]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

MonadPlus と guard 関数

リスト内包表記では、filter をかけることができた。

*Main> [x | x <- [1..50], '7' `elem` show x]
[7,17,27,37,47]

7のつく数字だけを結果として返している。

この動きを解明するためには、MonadPlus と guard 関数を知る必要がある。

MonadPlus はモノイドの性質を併せ持つモナドを表す型クラス。

class Monad m => MonadPlus m where
    mzero :: m a
    mplus :: m a -> m a -> m a

mzero は モノイドでいう mempty のこと。mplusmappend のこと。リストはモノイドなので、MonadPlus のインスタンスにできる。

instance MonadPlus [] where
    mzero = []
    mplus = (++)

リストのmzeroは失敗した非決定性計算を表現する。mplusは2つの非決定的値を1つの値に結合する。

guard 関数はこのように

guard :: (MonadPlus m) => Bool -> m ()
guard True = return ()
guard False = mzero

guard は真理値を引数にとる。引数がTrueの場合()を成功を表す最小限の文脈に入れる。引数がFalseなら失敗したモナド値を作る。

*Main> import Control.Monad
*Main Control.Monad> guard (5 > 2) :: Maybe ()
Just ()
*Main Control.Monad> guard (1 > 2) :: Maybe ()
Nothing
*Main Control.Monad> guard (5 > 2) :: [()]
[()]
*Main Control.Monad> guard (1 > 2) :: [()]
[]

リストモナドでは、guard を使って解の候補をふるい落とすことができる。

*Main Control.Monad> [1..50] >>= (\x -> guard ('7' `elem` show x) >> return x)
[7,17,27,37,47]

guard に >> をつなぐとどうなるか

*Main Control.Monad> guard (5 > 2) >> return "cool" :: [String]
["cool"]
*Main Control.Monad> guard (1 > 2) >> return "cool" :: [String]
[]

guard がやっていることは、引数がFalseなら直ちに失敗を投げる。Trueならダミーの値()が入っている成功を作る。という計算を続けてよいかの判断をやっている。

do 記法で書き直す

import           Control.Monad

sevensOnly :: [Int]
sevensOnly = do
    x <- [1..50]
    guard ('7' `elem` show x)
    return x

実行

*Main Control.Monad> sevensOnly
[7,17,27,37,47]

これと同義

*Main> [x | x <- [1..50], '7' `elem` show x]
[7,17,27,37,47]

所感

リスト内包表記はモナドのちからを使って実現されていたことが判明した。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る