プログラミングHaskellを読む (4)
第4章 関数定義
関数定義で使えるワザ
- 既存の関数を利用する
- if文
- 一番単純なパターンマッチ
- thenとelseが必須
- then, else節は型が同じ必要がある
- 必須なのでいわゆる「ぶらさがりelse問題」が発生しない
-- 絶対値 abs1 n = if n >= 0 then n else -n -- 整数の符号を返す signum1 n = if n < 0 then -1 else if n == 0 then 0 else 1
- ガード付きの等式
- ガードという論理式が並ぶ
- ガードに、=をつけて処理を書く
- 上から評価してTrueの部分の処理が実行される
- otherwiseはTrueと等価。
abs2 n | n >= 0 = n | otherwise = -n signum2 n | n < 0 = -1 | n == 0 = 0 | otherwise = 1
-- タプル・パターン fst :: (a, b) -> a fst x _ = x snd :: (a, b) -> b snd _ y = y -- リスト・パターン null :: [a] -> Bool null [] = True null _ = False head :: [a] => a head (x : _) = x {- head x : _ = x だとどうなるかというと、関数のほうがcons演算子より結合度が高いので (head x) : _ = x と解釈される。なので括弧は必須。 -} -- n+k・パターン pred 0 = 0 pred (n + 1) = n {- k以上の整数の時にマッチする。 n+kは将来使えなくなるかもしれない。 -}
- λ式
- 要するに無名関数
- カリー化を明確に表現できる
add x y = x + y add' = \ x -> (\ y -> x + y) -- λを使わない場合 const :: a -> b -> a const x _ = x {- a = const 10 カリー化されているのでaを呼び出せば常に10が返る -} -- λを使うと意図が明確 const' x = \ _ -> x
- セクション
-- λで形式化出来る。?は何らかの記号 (?) = \ x -> (\ y -> x ? y) (x ?) = \y -> x ? y (? x) = \x -> x ? y
練習問題
1. ライブラリ関数を使ったhalveの実装
halve :: [a] -> ([a], [a]) halve x = (take hl x, drop hl x) where hl = (length x) `div` 2
2. safetailを定義
-- 条件式 safetail :: [a] -> [a] safetail x = if null x then [] else tail x -- ガード付きの等式 safetail' :: [a] -> [a] safetail' x | null x = [] | otherwise = tail x -- パターンマッチ safetail'' :: [a] -> [a] safetail'' [] = [] safetail'' (_ : xs) = xs
私は一番パターンマッチが自然に感じます。
3. 論理和演算子or
or1 :: Bool -> Bool -> Bool True `or1` True = True False `or1` True = True True `or1` False = True False `or1` False = False or2 :: Bool -> Bool -> Bool False `or2` False = False _ `or2` _ = True or3 :: Bool -> Bool -> Bool False `or3` b = b True `or3` _ = True or4 :: Bool -> Bool -> Bool b `or4` c | b == c = b | otherwise = True
ついでに確認するための関数を作成してみた。
checkOr :: (Bool -> Bool -> Bool) -> [Bool] checkOr or = [a `or` b | a <- [True, False], b <- [True, False]]
*Main> checkOr (||) [True,True,True,False] *Main> checkOr or1 [True,True,True,False] *Main> checkOr or2 [True,True,True,False] *Main> checkOr or3 [True,True,True,False] *Main> checkOr or4 [True,True,True,False]
4. 論理演算子を条件式で再定義 (1)
{- True && True = True _ && _ = False -} and1 :: Bool -> Bool -> Bool and1 a b = if a then if b then True else False else False
答えをチェックするに当たり、よく考えたら、前問のcheckOrがそのままつかるので、汎用メソッドにしてみました。
checkBool :: (Bool -> Bool -> Bool) -> [Bool] checkBool op = [a `op` b | a <- [True, False], b <- [True, False]]
*Main> checkBool (&&) [True,False,False,False] *Main> checkBool and1 [True,False,False,False]
5. 論理演算子を条件式で再定義 (2)
{- True && b = b False && _ = False -} and2 :: Bool -> Bool -> Bool and2 a b = if a then b else False
6. カリー化された関数をラムダ式で表現
-- mult x y z = x * y * z mult = \x -> (\y -> (\z -> x * y * z))