前回の振り返り
2章 型を信じろ
- Char
- Bool
- Float
- Integer
など
型変数 どんな型も取りうる
型クラス 関数の集まり
- 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
- Haskellでは再帰がすごく重要
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' -- Non-exhaustive patterns in function charName
タプルのパターンマッチ
タプルでもパターンマッチできる。
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
'はダッシュと読むこともあるけど、プライムのほうがベター