第三章はHaskellの関数についての独特な構文についての説明になっている。
パターンマッチ
Haskellの関数では、特定のパターンにマッチした場合で、処理を分けることができる。以下のように書くと、7を取る場合と任意の数xを取る場合で処理を分岐することができる。
lucky :: Int -> String lucky 7 = "Lucky 7" lucky x = "Unlucky"
タプル、リストのパターンマッチはそれぞれ以下のように書くことができる。
addTuple :: (Int, Int) -> Int addTuple (x,y) = x+y -- 先頭の要素とそれ以外のリストに分けて取得できる head'' :: [Int] -> Int head'' (x:xs) = x
asパターン
パターンマッチで分解したいが、元の値も取得したい場合に使える。 参照したい元の名前@パターン
という構文。
firstLetter :: String -> String firstLetter all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]
ガード
関数を引数の値が満たす性質で場合わけをするときはガード構文が使える。 |
を使って表現する。
num x | x == 1 = "One" | x == 2 = "Two" | x == 3 = "Three" | otherwise = "Other number"
他の言語だとcase文が一番近いか。
ガードとは関係ないが、バッククォートによる中置記法で関数定義する例が載っていた。
myCompare :: (Ord a) => a -> a -> Ordering a `myCompare` b | a == b = EQ | a <= b = LT | otherwise = GT
ちなみにotherwiseがなくてもコンパイルは通るが、マッチするパターンがない場合はエラーになる。(パターンマッチも同様).
where
関数内で何がしかの計算結果を格納しておくための構文。
calcSum a b = sum where sum = a + b
変数を複数定義する場合は変数名のインデントを揃えなければいけない点に注意が必要。また、whereのスコープは関数内限定。さらに関数で複数のパターンマッチを定義している場合、whereの変数はパターンマッチ間で共有されない。あくまでwhereを記述したパターンマッチの中でのみ参照可能。
let
whereと同じように変数を束縛できる。 let bingings in expression
という構文。
calcSum a b = let sum = a + b in sum
whereとの違いは、whereが節であるのに対してletは式であること。whereは関数内でしか使えないが、letは単なる式であるため、あらゆる場所で使用できる。
ghci> (let nine = 5+4 in nine) *4 36
リスト内包表記で使える例
リスト内包表記でletを使うことで、フィルタや出力をスッキリ書くことができる。以下はbmiの計算と、その値によるフィルタリングを行なっている。
calcBmi xs = [bmi | (w,h) <- xs, let bmi = w/h^2, bmi > 25.0]
case式
引数のパターンマッチ、ガードと先に紹介したが、ちゃんとcaseもある。パターンマッチはcase式の糖衣構文。また、Haskellのcase はletと同様式のため、どこでも使える。
num x = case x of 1 -> "One" _ -> "other"