Haskell個人メモ :: 3.関数の構文


# シリーズ :: [Qiita記事]

[Haskell個人メモ :: 1.基本](http://qiita.com/YusukeHosonuma/items/41ad9346b6e3a4d3789c)
[Haskell個人メモ :: 2.型](http://qiita.com/YusukeHosonuma/items/b115de430aa0d3fc9205)
**Haskell個人メモ :: 3.関数の構文　←いまここ**
[Haskell個人メモ :: 4.再帰](http://qiita.com/YusukeHosonuma/items/c22171dae82430aa9504)

# パターンマッチ

## 基本

```haskell
-- | ラッキー？
lucky :: Int -> String -- Int型を引数に受け取り、String型を返す
lucky 7 = "Lucky!"
lucky x = "Unlucky..."
```

```haskell:ghci
lucky 7 -- "Lucky!"
lucky 8 -- "Unlucky..."
```

パターンマッチは上から順番に評価される。
上記では`x`に実際の値が束縛されるが、未使用なので通常は`_`で記述して値を捨てる。

## 再帰

```haskell
-- | 階乗を求める
factorial :: Int -> Int
factorial 0 = 1						-- 0! は 1
factorial n = n * factorial (n - 1)	-- n! は n * (n - 1)!
```

```haskell:ghci
factorial 5 -- 120
```


パターンマッチで再帰呼び出しをすることも可能。

## 考慮されていないケース

```haskell
fruits :: Char -> String
fruits 'A' = "Apple"
fruits 'B' = "Banana"
```

```haskell:ghci
fruits 'A' -- "Apple"
fruits 'B' -- "Banana"
fruits 'C' -- "*** Exception: baby.hs:(17,1)-(18,21): Non-exhaustive patterns in function fruits
```

パターンマッチで考慮されていない、つまり末端を抜けてしまった場合は実行時エラーになる。

## タプルのパターンマッチ

```haskell
add :: (Int, Int) -> (Int, Int) -> (Int, Int)
add (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
```

```haskell:ghci
add (1, 1) (2, 3) -- (3,4)
```

タプルは中身の値を直接マッチ（束縛）させることができる。

## リスト内包表記でのパターンマッチ

```haskell:ghci
let xs = [(1, 10), (2, 20), (3, 30)]
[ x + y | (x, y) <- xs ] -- [11,22,33]
```

## リストのパターンマッチ

```haskell
head' :: [a] -> a
head' []     = error "List is empty."
head' (x:xs) = x

tail' :: [a] -> [a]
tail' []     = error "List is empty."
tail' (x:xs) = xs
```

`x:xs`という形で「先頭の要素」と「残りの要素」で分解できる。
空のリストは`[]`でマッチできる。

```haskell
-- | リストの先頭から2つの要素を取得する
head2 :: [a] -> [a]
head2 []       = error "List is empty."
head2 (x:y:ys) = [x, y]
```

`x:y:ys`といった形にすることで、先頭以外の要素も分解して取得できる。

`x`や`xs`は命名規約ではないが、Haskellの慣習として（リストのパターンマッチでは特に）この名前が好んで使用される。
`x`は単一、`xs`は複数(s)という意味合いらしい。

## asパターンマッチ

```haskell
firstLetter :: String -> String
firstLetter s@(x:xs) = s ++ "'s first letter is " ++ [x]
```

```haskell:ghci
firstLetter "Hello" -- "Hello's first letter is H"
```

`全体@パターン`と書くことで、パターンで分解しつつ、全体（元の値）にもアクセスできる。

# ガード

```haskell
myCompare :: (Ord a) => a -> a -> Ordering
a `myCompare` b
  | a == b    = EQ
  | a <= b    = LT
  | otherwise = GT
```

値の「構造」ではなく、「性質」で場合分けする場合はパターンマッチではなくガードを利用する。
`if-then-else`だとネストが深くなる場合などに有効。

書式は以下のとおり。

```
式
  | 条件1 = 条件1にマッチしたとの処理
  | 条件2 = 条件2にマッチしたとの処理
  | otherwise = いずれの条件にもマッチしなかった時の処理
```

# where

## 変数の束縛

```haskell
-- | BMI判定（体重kg、身長cm）
judgeBMI :: Double -> Double -> String
judgeBMI weight height
  | bmi < 18.5 = "低体重" ++ show bmi
  | bmi < 25.0 = "標準"
  | bmi < 30.0 = "肥満度1"
  | bmi < 35.0 = "肥満度2"
  | bmi < 40.0 = "肥満度3"
  | otherwise  = "肥満度4"
  where
    bmi     = weight / (heightM * heightM)
    heightM = height / 100
```

```haskell:ghci
putStrLn $ judgeBMI 67 160.0 -- 肥満度1
```

関数の末尾に`where`、その後の行に`変数 = 計算式`と記述することで一時変数（的な）ものを作れる。
`where`内で、`where`内で定義した別の変数を参照することも可能（上記では`heightM`）。

書式は以下のとおり。インデントは揃っていないとNG。

```haskell
where
  変数1 = 式
  変数2 = 式
  ...
```

## 関数の束縛

```haskell
hello :: String -> String
hello name = "Hello, " ++ mr name
  where
    mr name = "Mr." ++ name
```

同様に関数も束縛できる。

## where内でのパターンマッチ

```haskell
-- | イニシャルを取得
initials :: String -> String -> String
initials firstname lastname = [l] ++ "." ++ [f]
  where
    (f:_) = firstname
    (l:_) = lastname
```

```haskell:ghci
initials "Yusuke" "Hosonuma" -- "H.Y"
```

where内でもパターンマッチできる。

# let

## 基本

```haskell:ghci
[let square x = x * x in (square 3, square 4, square 5)] -- [(9,16,25)]
```

`let 変数/関数 in 式`という書式で、ローカルな変数や関数（上記では`square`）を定義できる。
`let`は式なので結果を返す（上記ではタプル）。

## whereとの違い

`where`と似ているが以下の点が異なる。
- どこでも書ける。
- `where`ではガードをまたぐことが出来るが、`let`で定義したものは`in`の中でしか参照できない。

好みの問題もあるが、基本的には`where`を使って、必要な場合は`let`を使うのが良さそう。

## リスト内包表記内でのlet

```haskell
-- | 複数のBMIを計算
bmis :: [(Double, Double)] -> [Double]
bmis xs = [ bmi | (w, h) <- xs, let hm  = h / 100
                                    bmi = w / (hm * hm) ]
```

```haskell:ghci
bmis [(47, 160), (67, 170)] -- [18.359374999999996,23.18339100346021]
```

リスト内包表記内で`let`で宣言したものは、出力部（`|`の前）と`let`より先のリスト内包表記内で参照できる。

# case式

```haskell
sayNumber :: Int -> String
sayNumber n = (show n) ++ " is "
              ++ case n of
                1 -> "One"
                2 -> "Two"
                _ -> "Number!"
```

```haskell:ghci
sayNumber 1 -- "1 is One"
sayNumber 2 -- "2 is Two"
sayNumber 3 -- "3 is Number!"
```

`case`式はどこでも使えるパターンマッチ。

関数のパターンマッチと同等のことが式としてどこでも記述できる。
（関数のパターンマッチは`case`式のシンタックスシュガー）

書式は以下のとおり。

```haskell
case 値 of
  パターン1 -> 処理1
  パターン2 -> 処理2
  ...  
```

ガードの場合、条件式と処理を分けるのは`=`であったが、こちらは`->`である点に注意。