前回の振り返り
2章 型を信じろ
など
型変数 どんな型も取りうる
型クラス 関数の集まり
- Eq
- Ord
- Show
- Read
- Enum
- Bounded
- Num
- Floating
- Integral
例 (+)
class Num
Prelude> :t (+)
(+) :: Num a => a -> a -> a
全部同じ型でなければならない。
ただし、下のように型推論してくれるところは、あるよ。
Prelude> 1.0 + 10
11.0
ただしこちらはエラー
Prelude> (1.0 + 10) `div` 10
理由は、型が合わないから
Prelude> :t div
div :: Integral a => a -> a -> a
こういうのはおーけー
Prelude> (floor 1.0) `div` 3
0
Prelude> (floor 10.0) `div` 3
3
H本の輪読
第3章 関数の構文
- Haskellの関数を確認する
- 値の分解
- if/else
- 計算データの一時的な保存
パターンマッチ
- パターンにしたがってデータを分解
- Haskellではパターンマッチがなまら多い
sayMe :: Int -> String
sayMe 1 = "One!"
sayMe 2 = "Two!"
main = do
print $ sayMe 2
factorial :: Int -> Int
factorial 0 = 1
factorial n = n * factorial (n - 1)
main = do
print $ factorial 10
- 上の例でsayMe 3とかやるとnon-exhaustive patterns(パターンが網羅的でない)と文句を言われる
charName :: Char -> String
charName 'a' = "Alpha"
main = do
print $ charName 'b'
タプルのパターンマッチ
タプルでもパターンマッチできる。
addVectors :: (Double, Double) -> (Double, Double) -> (Double, Double)
addVectors (x1, y1) (x1, y2) = (x1 + x2, y1 + y2)
main = do
print $ addVectors (1.0,0.0) (2.0,1.5)
リストのパターンマッチとリスト内包表記
リストでもパターンマッチできる。
Prelude> let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)]
Prelude> [x*100+3 | (x,3) <- xs]
[103,403,503]
head' :: [a] -> a
head' [] = error "Empty"
head' (x:_) = x
main = do
print $ head' []
print $ head' [3,4,5]
asパターン
パターンマッチの対象になった値自体も参照
firstLetter :: String -> String
firstLetter "" = "Empty String"
firstLetter all@(x:xs) = all
*Main> firstLetter "hogehoge"
"hogehoge"
ガード
ifとガードは似ている。命令形言語における巨大なif/elseの連鎖
パイプとそれに続く心理知識、評価されたときに使われる関数の本体が続く。
if/elseで続けるよりガードでやったほうが可読性は高い
bmiTell :: Double -> String
bmiTell bmi
| bmi <= 18.5 = "18.5"
| bmi <= 25.0 = "25.0"
| otherwise = "hoge"
max' :: (Ord a) => a -> a -> a
max' a b
| a <= b = b
| otherwise = a
where
計算の中間結果に名前をつけて繰り返しを避ける
where' :: Double -> Double -> String
where' a b
| a / b <= 1.0 = "less than 1"
| a / b <= 2.0 = "less than 2"
| otherwise = "bigger"
a / bをwhereの中で変数cに束縛する
where' :: Double -> Double -> String
where' a b
| c <= 1.0 = "less than 1"
| c <= 2.0 = "less than 2"
| otherwise = "bigger"
where
c = a / b
→可読性とプログラム速度の向上
whereのスコープ
whereで定義されたスコープはその関数の中でしか見れない。ガードは超えられるがパターンは超えられない
greet :: Int -> Int -> String
greet 1 1 = name ++ "hoge"
greet 1 2 = name ++ "fuga"
greet 1 3 = name ++ "fuga"
where
name = "bar"
パターンマッチとwhere
パターンマッチをwhereの中でも書ける
whereブロックの中の関数
whereの中で関数も定義できる
let it be
letは式で、whereは節。
- 式なので値が含まれる
- let bindings in expression
- letで定義した変数はletの中で参照することができる
リスト内包表記内でのlet
- リスト内包表記の(w,h) <- xs部分はジェネレータと呼ばれる
- whereとlet
GHCiでのlet
GHCiではlet式のin部分は省略できて、それ以降の対話的なセッション全体から見えるようになる
case
case expression of pattern -> result
pattern -> result
pattern -> result
pattern -> result
'はダッシュと読むこともあるけど、プライムのほうがベター