読者です 読者をやめる 読者になる 読者になる

by shigemk2

当面は技術的なことしか書かない

第2期 第22回 H本読書会 in 秋葉原 #readhbon

Haskell

readhbon.doorkeeper.jp

do記法

doを使うパターンと使わないパターンで比べてみる。

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

routine :: Maybe Pole
routine = do
    start <- return (0, 0)
    first <- landLeft 2 start
    second <- landRight 2 first
    landLeft 1 second

-- doを使わないと、こんな感じで超だるい
-- routine :: Maybe Pole
-- routine = case landLeft 1 (0, 0) of
--               Nothing -> Nothing
--               Just pole1 -> case landRight 4 pole1 of
--                                 Nothing -> Nothing
--                                 Just pole2 -> case landLeft 2 pole2 of
--                                                   Nothing -> Nothing
--                                                   Just pole3 -> landLeft 1 pole3


main = do
    print $ landLeft 2 (0, 0)
    print $ landLeft 1 (1, 2)
    print $ landRight (-1) (1, 2)
    print $ landRight 10 (0, 3)
    print $ landRight 1 (0, 0) >>= landLeft 2
    print $ routine

途中でバナナ

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

-- 途中でバナナを踏ませる
routine :: Maybe Pole
routine = do
    start <- return (0, 0)
    first <- landLeft 2 start
    Nothing
    second <- landRight 2 first
    landLeft 1 second

main = do
    print $ routine -- Nothing

パターンマッチが使える

doの中でパターンマッチが使える。

justH :: Maybe Char
justH = do
    (x:xs) <- Just "hello"
    return x

main = do
    print $ justH

これが失敗するパターンはどうなるのか。

wopwop :: Maybe Char
wopwop = do
    (x:xs) <- Just ""
    return x

main = do
    print $ wopwop

do式の中でパターンマッチが失敗した場合、 Monad 型クラスの一員であるfail 関数が使われるとのこと。do式を使わずにパターンマッチしないパターンマッチを使うとエラーになる。

-- do 式の中でパターンマッチが失敗した場合、 Monad 型クラスの一員であるfail 関数が使われる
wopwop :: Maybe Char
wopwop = do
    (x:xs) <- Just ""
    return x

main = do
    print $ Just "Hello" >>= \(x:xs) -> Just x
    -- Non-exhaustive patterns in lambda
    -- failのデフォルト実装だと死ぬ
    -- print $ Just "" >>= \(x:xs) -> Just x
    print $ wopwop

リストモナド

リストをアプリカティブ・スタイルで使うと、非決定性を表現している

main = do
    print $ (*) <$> [1,2,3] <*> [10,100,1000]
main = do
    print $ (*) <$> [1,2,3] <*> [10,100,1000]
    print $ [3,4,5] >>= \x -> [x,-x]
    print $ [] >>= \x -> ["bad", "mad", "rad"]
    -- [1,2,3] >>= \x -> []

文脈は非決定性

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

main = do
    print $ listOfTuples

do記法とリスト内包表記

-- リスト内包表記はリストモナドの構文糖衣にすぎない
main = do
    print $ [ (n, ch) | n <- [1,2], ch <- ['a','b'] ]

MonadPlusとガード

モノイドの性質を併せ持つのがモナドプラス。

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

guard関数

guard :: (MonadPlus m) => Bool -> m ()
guard True = return ()
guard False = mzero
main = do
    print $ [ x | x <- [1..50], '7' `elem` show x ]
Prelude Control.Monad> guard (5 > 2) :: Maybe ()
Just ()
Prelude Control.Monad> guard (1 > 2) :: Maybe ()
Nothing
Prelude Control.Monad> guard (5 > 2) :: [()]
[()]
Prelude Control.Monad> guard (1 > 2) :: [()]
[]

騎士の旅

import Control.Monad

type KnightPos = (Int, Int)

moveKnight :: KnightPos -> [KnightPos]
moveKnight (c,r) = do
    (c', r') <- [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
                ,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
                ]
    guard (c' `elem` [1..8] && r' `elem` [1..8])
    return (c', r')

in3 :: KnightPos -> [KnightPos]
in3 start = do
    first <- moveKnight start
    second <- moveKnight first
    moveKnight second

canReachIn3 :: KnightPos -> KnightPos -> Bool
canReachIn3 start end = end `elem` in3 start

main = do
    print $ moveKnight (6,2)
    print $ moveKnight (8,1)
    print $ (6, 2) `canReachIn3` (6, 1)
    print $ (6, 2) `canReachIn3` (7, 3)

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

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