第3章は関数の定義の仕方について。
パターンマッチ
パターンマッチはパターンを上から読んで該当した関数を実行する。
fizzbuzz :: Int -> String fizzbuzz 1 = "" fizzbuzz 2 = "" fizzbuzz 3 = "fizz" ...
タプル、リストも使える。
-- タプルでのパターンマッチ addVectors :: (Double, Double) -> (Double, Double) -> (Double, Double) addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2) first :: (a, b, c) -> a first (x, _, _) = x -- リストでのパターンマッチ head' :: [a] -> a head' [] = error "Can't call head on empty list, dummy" head' (x:_) = x tell :: (Show a) => [a] -> String tell [] = "empty" tell (x:[]) = "one element" ++ show x tell (x:y:[]) = "two element" ++ show x ++ show y tell (x:y:_) = "too long"
Asパターン
普通のパターンマッチでは、マッチした中身のみ取り出せて最初に受け取った変数全体を取り出せない。@マークを使って変数全体も取り出せる。
head'' :: String -> String head'' "" = "empty" head'' all@(x:xs) = "head of " ++ show all ++ "is" ++ show x
ガード
第1章のまとめで以下のようなコードを書いた。
myfizz x = if div x 15 == 0 then "fizzbuzz" else if div x 3 == 0 then "fizz" else if div x 5 == 0 then "buzz" else show x
これをガードを使って書き直すと、
fizzbuzz :: Int -> String fizzbuzz x | mod x 15 == 0 = "fizzbuzz" | mod x 3 == 0 = "fizz" | mod x 5 == 0 = "buzz" | otherwise = show x
となり、より読みやすい。引数が増えても同じ。
where
bmitell :: Double -> Double -> String bmitell weight height | bmi <= 18 = "underweight" | bmi <= 25 = "normal" | bmi <= 30 = "fat" | otherwise = "too fat" where bmi = weight / height ^ 2
と書けば何度も同じ式を書かなくてすむ。
whereは便利だが、定義した変数のスコープは直上のパターンマッチのみであることに注意(ガードはOK)。パターンマッチもwhere中で使うことができる。
let
whereは上で説明したように便利だが、最後に定義が来てしまうことと、スコープが狭いことが問題になりうる。そういう場合にletを使う。
cylinder :: Double -> Double -> Double cylinder r h = let sideArea = 2 * pi * r * h topArea = pi * r ^ 2 in sideArea + 2 * topArea
letが定義、inが値になる。
(let a = 1; b = 2 in a * b, let c = 3 in c**c) [let (a, b, c) = (1, 2, 3) in a * b * c]
ghciでも使える。
case
caseはコード中のどこでも使える。
head :: [a] -> a head xs = case xs of [] -> error "empty" (x:_) -> x
以下は等価なコード
describeList :: [a] -> String describeList ls = "the list is " ++ case ls of [] -> "empty" [x] -> "singleton" xs -> "longer" describeList ls = "the list is " ++ what ls where what [] = "empty" what [x] = "singleton" what xs = "longer"
whatという関数を定義してwhereと組み合わせて使うことでwhereでもcaseのように後ろにまとめて定義できる。