by shigemk2

当面は技術的なことしか書かない

第2期 第3回 H本読書会 in 秋葉原 第3章 #readhbon

前回の振り返り

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

'はダッシュと読むこともあるけど、プライムのほうがベター