プログラミングHaskell第4章(関数定義)まとめ
cons演算子「:」
- 既存のリストの先頭に新しい要素を追加したリストを作成する
- cons演算子は右結合
Prelude> :t (:) (:) :: a -> [a] -> [a] Prelude> 1 : [2, 3] [1,2,3] Prelude> 1 : (2 : (3 : [])) -- [1, 2, 3]はこれの略記 [1,2,3] Prelude> 1 : 2 : 3 : [] -- 右結合なので括弧はいらない [1,2,3]
関数演算子の変換
a -> b -> c
のような関数を2項演算子のように使ったりその逆もできる。
- 演算子関数
関数をバックスラッシュで囲む
div 13 2 -- div :: Integral a => a -> a -> a 13 `div` 2 -- 2項演算子として使う
- 関数演算子
演算子を()で囲む(セクション)。
True && False -- &&は2項演算子 (&&) True False -- (&&) :: Bool -> Bool -> Bool
関数定義
関数名 :: 関数の型
関数定義
ghciで関数定義す場合は、letをつけて、一行で定義するか:{:}でくくって複数行で書く。レイアウト規則に注意
Prelude> :{ Prelude| let alwaysTrue :: Bool -> Bool Prelude| alwaysTrue _ = True Prelude| :} <interactive>:18:2: parse error on input `alwaysTrue' Prelude> :{ Prelude| let alwaysTrue :: Bool -> Bool Prelude| alwaysTrue _ = True Prelude| :} Prelude> alwaysTrue False True
条件式(if文)
- if文による定義では、else節は省略不可能
- レイアウト規則にも注意
signum :: Int -> Int signnum n = if n < 0 then -1 else if n == 0 then 0 else 1
ガード付きの等式
- 最初のガードがTrueなら最初の結果が選ばれ、そうでないなら次のガードが評価される。
- ガードotherwiseが使用できる。標準ライブラリで単にotherwise = True
- ガード付きの等式による定義では、ガードotherwiseを必ずしも置く必要なないが、どのガードもTrueにならなければエラーが発生する。
signnum n | n < 0 = -1 | n == 0 = 0 | otherwise = 1
ガードと戻り値の順番が、下のような数学的な記述と逆なのを除くと結構わかりやすい構文ですね。
\( signnum(n)= \begin{cases} -1 & (n < 0) \\ 0 & (n = 0) \\ 1 & (otherwise) \end{cases} \)
パターンマッチ
値、変数、_(ワイルドカード)パターン
- 同じ型の候補の中からマッチするものを返すよう関数定義できる。
- 最初のパターンにマッチすれば最初の結果が選ばれ、そうでないなら次のパターンをみる。
ワイルドカードと変数の違い
(&&) :: Bool -> Bool -> Bool True && b = b False && _ = False
-- ◯ 同じ等式にワイルドカードが複数あるのはおk True && True = True _ && _ = False
-- × 同じ等式に同じ引数名があるのはNG b && b = b _ && _ = False
-- ◯ガード付き等式を使えばこのようにかける b && c | b == c = b | otherwise = False
タプル・パターン
fst :: (a, b) -> a fst (x, _) = x snd :: (a, b) -> b snd (_, y) = y
リスト・パターン
-- 要素数が3のリストで頭が'a'ならTrue、そうでないならFalse axxTest :: [Char] -> Bool axxTest ['a', _, _] = True axxTest _ = False -- 先頭要素が'a`のリストならTrue、そうでないならFalse firstATest :: [Char] -> Bool firstATest ('a' : _) = True -- 関数適用は演算子より優先順位が高いため()でくくる必要がある firstATest _ = False
パターン
- は整数、は正の整数定数
- パターンは以上の整数にしかマッチしない
- 関数適用の方が優先順位が高いので()でくくる必要がある
※Haskell 2010で仕様から削られてます。使わないほうが良いでしょう。
ghciで使う場合はXNPlusKPatternsオプションを使う
ghci -XNPlusKPatterns
ファイルにオプションを書けばコマンドラインオプションは不要
{-# LANGUAGE NPlusKPatterns #-} pred :: Int -> Int pred 0 = 0 pred (n + 1) = n -- 負の数にはマッチしないのでpred (-1)はNG
where節
where節の直前で定義した関数内でwhere節で定義した局所関数や局所変数を使う事ができる。レイアウト規則に注意。
halve :: [a] -> ([a], [a]) halve xs = (take l xs, drop l xs) where l = length xs `div` 2 -- splitAt :: Int -> [a] -> ([a], [a])を使うと halve xs = splitAt (length xs `div` 2) xs
式(lambda expression)
バックスラッシュで式(無名関数)が使用できる。
- カリー化された関数の形式的な意味づけに利用できる
mul3 x y z = x*y*z mul3 = \x -> (\y -> (\z -> x*y*z)) mul3 = (\x y z -> x*y*z) -- こう書くこともできる
- 関数を返す関数定義に便利
-- const関数は、「引数が何であっても指定した定数 x を返す関数」を返す const :: a -> b -> a const x _ = x -- 関数を返すことがわかりやすい書き方 const :: a -> (b -> a) const x = \_ -> x
- where節に書く局所関数を簡略記述できる
-- map :: (a -> b) -> [a] -> [b] -- map関数は、リストの各要素に関数を適用したリストを返す evens :: Int -> [Int] evens n = map (\x -> x*2) [0..n-1]
セクション
を演算子とすると、式をセクションという。
主な利用方法