すごいHaskellたのしく学ぼうでHaskellことはじめ2

前回までのあらすじ

すごいHaskellたのしく学ぼうでHaskellことはじめ

GitHub Pagesの方にリンクを貼ってみよう。

参考

  • すごいHaskellたのしく学ぼう

3章 関数の構文

パターンマッチ

  • 関数の場合分け
  • 上から順番に調べられる
  • 全てに合致するパターンを最後に入れておくと吉
lucky 7 = "SEVEN !"
lucky x = "Other Number"
*Main> lucky 1
"Other Number"
*Main> lucky 7
"SEVEN !"
*Main> lucky 9
"Other Number"

上から順番なので、一行目と二行目をひっくり返すと、引数に7を渡しても “SEVEN !” が出力されなくなる。

タプルのパターンマッチ

addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
*Main> addVectors (1,3) (5,9)
(6,12)

リストのパターンマッチ

x:xs … リストの先頭要素xと残りのxs(リスト [1,2,3]1:2:3:[] と表せる)

*Main> [1,2,3,4]
[1,2,3,4]
*Main> 1:[2,3,4]
[1,2,3,4]

それを踏まえてhead関数の独自実装。

  • error はランタイムエラーを発生させる
head' [] = error "Error Dayo"
head' (x:xs) = x
*Main> head' "He is ..."
'H'
*Main> head' [3,2,4]
3
*Main> head' []
*** Exception: Error Dayo
*Main> head' ""
*** Exception: Error Dayo

上記のコードでは、 xs は一回も使っていない = どうでもいい値 なので _ とする事もできる。

as

  • 引数をパターンマッチのパターンに分解するが、その値自体も使う場合
  • パターンの前に 変数@ を追加
firstLetter "" = "Empty"
firstLetter all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]
*Main> firstLetter "Watashi"
"The first letter of Watashi is W"
*Main> firstLetter ""
"Empty"

ガード

  • 場合分け
  • if else のようなもの
  • ひとつ以上のスペース でインデントする必要がある
  • 上から順番に
  • 全てキャッチするのは otherwise
bmiTell bmi
 | bmi <= 18.5 = "Yase!"
 | bmi <= 25.0 = "Normal!"
 | bmi <= 30.0 = "Debuya!"
 | otherwise   = "Otherwise!"
*Main> bmiTell 1
"Yase!"
*Main> bmiTell 19
"Normal!"
*Main> bmiTell 26
"Debuya!"
*Main> bmiTell 31
"Otherwise!"

Where

BMIって「体重(kg) / (身長(m) ^ 2)」らしいので、上記の関数をこんな風に変えられる。

bmiTell wei hei
 | wei / hei ^ 2 <= 18.5 = "Yase!"
 | wei / hei ^ 2 <= 25.0 = "Normal!"
 | wei / hei ^ 2 <= 30.0 = "Debuya!"
 | otherwise   = "Otherwise!"
*Main> bmiTell 40 1.5
"Yase!"
*Main> bmiTell 50 1.5
"Normal!"
*Main> bmiTell 60 1.5
"Debuya!"
*Main> bmiTell 90 1.5
"Otherwise!"

ただし、上記のガード式だとBMI算出を無駄に繰り返している。whereを使って置き換える。

bmiTell wei hei
 | bmi <= 18.5 = "Yase!"
 | bmi <= 25.0 = "Normal!"
 | bmi <= 30.0 = "Debuya!"
 | otherwise   = "Otherwise!"
 where bmi = wei / hei ^ 2

同一パターンの中でしか使えない。

greet "Juan"     = hell ++ " Juan"
greet "Fernando" = hell ++ " Fernando"
greet name       = hell ++ " " ++ name
 where hell = "Hell."

コンパイルすると20, 21行目でNot in scope。

xxx.hs:20:20: Not in scope: `hell'

xxx.hs:21:20: Not in scope: `hell'

let

  • let … in …
  • whereと似ている
  • letは式、whereはそうじゃない
cylinder r h =
 let sideArea = 2 * pi * r * h
     topArea  = pi * r ^ 2
 in  sideArea + 2 * topArea
*Main> cylinder 4 3
175.92918860102841

case

  • case … of … -> …
  • caseも式
head'' [] = error "Error"
head'' (x:_) = x

head''' xs = case xs of []    -> error "Error"
                        (x:_) -> x
*Main> head'' [2,3,4,5]
2
*Main> head''' [2,3,4,5]
2
*Main> head'' []
*** Exception: Error
*Main> head''' []
*** Exception: Error