前回までのあらすじ
すごい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