tak0kadaの何でもノート

発声練習、生存確認用。

医学関連は 医学ノート

「Learn You a Haskell for Great Good!」第3章を読んだ

第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のように後ろにまとめて定義できる。