Haskell を使ってみる 7 (ガード)

前回の続き

Haskell を使ってみる 6 (パターンマッチ) - kntmr-blog

ガード

ガードは引数の値が満たす性質で処理を分岐させるときに使う。パイプ文字 (|) と条件式と関数本体を組み合わせて記述する。(パターンは引数の構造で条件を分岐させるもの)

-- 階乗を求める関数
fact :: Integer -> Integer
fact n
    | n < 0 = error "error!!"
    | n == 0 = 1
    | otherwise = n * fact(n - 1)

*Main> fact 3
6

where

where キーワードは計算結果を変数に束縛するときに使う。where で定義した変数のスコープはその関数内のみ。

-- where キーワードを使わない場合
totalCheck :: Integer -> Integer -> String
totalCheck x y
    | x + y < 10 = "less than 10"
    | x + y < 100 = "less than 100"
    | otherwise = "a large number"

-- where キーワードを使う場合
totalCheck' :: Integer -> Integer -> String
totalCheck' x y
    | total < 10 = "less than 10"
    | total < 100 = "less than 100"
    | otherwise = "a large number"
    where total = x + y

where キーワードの中でパターンマッチを使うことができる。

initials :: String -> String -> String
initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
    where (f:_) = firstname
          (l:_) = lastname

let

let 式では関数のどこでも変数を束縛することができる。let 式自身が式であり、変数の束縛は局所的でガード間で共有されない。(where キーワードは関数の終わりで変数を束縛する)

let で変数に束縛し、続く in に式を記述する。

-- ローカルスコープに関数を作ることができる
Prelude> [let square x = x * x in (square 2, square 3, square 4)]
[(4,9,16)]

-- セミコロン区切りで複数の変数に束縛できる
Prelude> (let a = 10; b = 20; c = 30 in a * b * c, let foo = "Hello"; bar = "Haskell" in foo ++ " " ++ bar)
(6000,"Hello Haskell")

-- let 式とパターンマッチでタプルを要素に分解して変数に束縛できる
Prelude> (let (a, b, c) = (1, 2, 3) in a + b + c) * 100
600

リスト内包表記と let 式の組み合わせ。述語のように使っているが、フィルタしているのではなく計算結果を変数に束縛している。

-- リストから値を受け取り、計算結果を area に束縛する
calcCircleArea :: [Double] -> [Double]
calcCircleArea xs = [area | r <- xs, let area = r * r * 3.14]

case

case 式ではコード中のどこでもパターンマッチを使うことができる。変数で指定した値に基づいてコードブロックを評価する。case 式は関数の引数に対するパターンマッチと似ている。(実際、case 式の糖衣構文になっている)

case 式に合致するパターンが見つからない場合はランタイムエラーが発生する。

-- 関数の引数に対するパターンマッチ
head' :: [a] -> a
head' [] = error "empty list"
head' (x:_) = x

-- case 式
head'' :: [a] -> a
head'' xs = case xs of [] -> error "empty list"
                       (x:_) -> x

引数によるパターンマッチは関数定義のときしか使えない。case 式では、式の途中でパターンマッチを使うことができる。

checkList :: [a] -> String
checkList list = "This list is "
               ++ case list of [] -> "empty."
                               [x] -> "a singleton list."
                               xs -> "a longer list."

-- case 式の代わりに where を使う場合
checkList' :: [a] -> String
checkList' list = "This list is " ++ what list
    where what [] = "empty."
          what [x] = "a singleton list."
          what xs = "a longer list."

今回はガードのあれこれについて。