ElixirのGETTING STARTED（5.case, cond and if）をやってみた

## URL
http://elixir-lang.org/getting-started/case-cond-and-if.html

## 試した環境
* Ubuntu Server 14.04 LTS 
* Erlang/OTP 18
* Elixir 1.0.4

## case, cond and if

### case

caseでマッチするのが見つかるまで複数のパターンについて比較することができる。

```sh
iex> case { 1, 2, 3 } do                                               
...> {4,5,6} -> "この節はマッチしない"                                 
...> {1,x,3} -> "この節にマッチし、xは2にバインドされる"                     
...> _ -> "この節はどんな値でもマッチする"              
...> end
"この節にマッチし、xは2にバインドされる"
```

この時、もし既に束縛した値とパターンマッチさせたい場合は、```^```演算子を使う。

```sh
iex> x = 1
1
iex> case 10 do
...> ^x -> "マッチしない"
...> _ -> "マッチする"
...> end
"マッチする"
```

``` ^x ```にパターンマッチさせる場合のサンプル。

```sh
iex> case {1, 3} do
...> {^x, y} -> "マッチして、yは3にバイドされる"    
...> _ -> "大抵ここでどれにもマッチしない場合の処理をする"
...> end
"マッチして、yは3にバイドされる"
```

節ではガードを使うことで条件を追加することができる。

```sh
iex> case {1, 2, 3} do
...> {1, x, 3} when x > 0 -> "xが0より大きい場合にマッチする"  
...> _ -> "ここではマッチしない"
...> end
"xが0より大きい場合にマッチする"
```

```sh
iex> case {1, 2, 3} do                                       
...> {1, x, 3} when x < 0 -> "xが0より小さい場合にマッチする"    
...> _ -> "ここではマッチする"                               
...> end
"ここではマッチする"
```

### Expressions in guard clauses

Erlang VMではガード中での式は限られている。

* 比較演算子 ```==```, ```!=```, ```===```, ```!==```, ```>```, ```<```, ```<=```, ```>=```
* 短絡boolean演算子 ```and```, ```or```
* 否定演算子 ```!```
* 左側がリテラルの場合の ```<>```, ```++```
* ```in```演算子
* 以下の型チェック関数は全てガード中で使える
 * ```is_atom/1```
 * ```is_binary/1```
 * ```is_bitstring/1```
 * ```is_boolean/1```
 * ```is_float/1```
 * ```is_function/1```
 * ```is_function/2```
 * ```is_integer/1```
 * ```is_list/1```
 * ```is_map/1```
 * ```is_number/1```
 * ```is_pid/1```
 * ```is_port/1```
 * ```is_reference/1```
 * ```is_tuple/1```
* 次の関数もガード中で使える
 * ```abs(number)```
 * ```bit_size(bitstring)```
 * ```byte_size(bitstring)```
 * ```div(integer,integer)```
 * ```elem(tuple, n)```
 * ```hd(list)```
 * ```length(list)```
 * ```map_size(map)```
 * ```node()```
 * ```node(pid | ref | port)```
 * ```round(number)```
 * ```self()```
 * ```trunc(number)```
 * ```tuple_size(number)```

ガード中で起きたエラーは単にガードの失敗となり、ガードの外に影響しない。

```sh
iex> case 1 do
...> x when hd(x) -> "マッチしない"
...> x -> "#{x} を使える"
...> end
"1 を使える"
```

もし、どの節にもマッチしない場合は、エラーが起きる。

```sh
iex> case :ok do
...>  :error -> "マッチしない"
...>  :ng -> "これもマッチしない"
...> end
** (CaseClauseError) no case clause matching: :ok
```

無名関数も節やガードを持つことができる。

```sh
iex> f = fn
...> x, y when x > 0 -> x + y
...> x, y -> x * y
...> end
#Function<12.54118792/2 in :erl_eval.expr/5>
iex> f.(1, 3)
4
iex> f.(-1, 3)
-3
```

無名関数の各節の引数は同じでなければならない。それ以外の場合はエラーが起きる。

```sh
iex> f = fn
...> x, y when x > 0 -> x + y
...> x, y, z -> x + y + z
...> end
** (CompileError) iex:12: cannot mix clauses with different arities in function definition
    (elixir) src/elixir_translator.erl:17: :elixir_translator.translate/2
```

### cond

```case``` は異なる値に対して、１つの値をマッチせる場合に役に立つ。
しかし、多くの状況で、様々な条件をチェックして```true```に評価される最初のものを見つけたいことがある。このような場合に```cond```が使える。

```sh
iex> cond do
...> 2 + 2 == 5 -> "trueにならない"
...> 2 * 2 == 3 -> "これもtrueにならない"
...> 1 + 1 == 2 -> "これはtrueになる"
...> end
"これはtrueになる"
```

これは多くの命令型言語の```else if```文と同じ（利用する頻度は少ないが）。

もし、trueになる条件がなければエラーになる。

```sh
iex> cond do                              
...>  2 * 2 == 3 -> "これもtrueにならない"
...>  2 * 2 == 3 -> "これもtrueにならない"
...> end
** (CondClauseError) no cond clause evaluated to a true value
```

そのため、最後に```true```を加えることで対応する必要がある場合がある。

```sh
iex> cond do 
...>  2 + 2 == 5 -> "これはtrueにならない"
...>  2 * 2 == 3 -> "これもtrueにならない"
...>  true -> "常にtrueになる"
...> end
"常にtrueになる"
```

最後に、```cond```では```nil```と```false```以外はどんな値でも```true```とされることを気をつける

```sh
iex> cond do 
...> hd([1,2,3]) -> "1はtrueとされる"
...> end
"1はtrueとされる
```

```sh
iex> cond do    
...> hd([:a, :b, :c]) -> "atomもtrue"
...> end
"atomもtrue"
```

下記は、aが未定義の関数として処理されるためエラーになる。

```sh
iex> cond do                         
...> hd([a,b,c]) -> "これはエラー"
...> end
** (RuntimeError) undefined function: a/0
```

### if and unless

```case``` 、 ```cond``` の他に、Elixirは１つの条件をチェックする必要がある場合に役に立つ```if/2``` と ```unless/2``` のマクロを提供している。

```sh
iex> if 2 == 1 + 1 do 
...>  "これは通る"     
...> end
"これは通る"
iex> if 2 == 1 - 1 do 
...> "これは通らない"
...> end
nil
```

```if/2``` に与えた条件が ```false``` か ```nil``` を返す場合は、```do``` と ```end``` で囲まれた部分は実行されず、単に```nil```が返却される。 ```if/2``` に与えた条件がfalseかnil以外の場合は、```do``` と ```end``` で囲まれた部分が実行される。
```false``` か ```nil``` 以外なので、例えば、与えた条件がatomを返しても ```do``` と ```end``` の部分は実行される

```sh
iex> if :atom do                                         
...> "これは通る"
...> end
"これは通る"
```

反対に、```unless/2```に与えた条件が```false```か```nil```の場合、```do```と```end```で囲まれた部分は実行され、それ以外の場合は```nil```を返す。

```sh
iex> unless false do
...> "これは通る"
...> end
"これは通る"
iex> unless true do 
...> "これは通らない"
...> end
nil
```

```sh
iex> unless 2 == 1 + 1 do 
...> "これは通らない"     
...> end
nil
iex> unless 2 == 1 - 1 do 
...> "これは通る"         
...> end
"これは通る"
iex> unless :atom do 
...> "これは通らない"
...> end
nil
```

```if/2``` と ```unless/2``` は、```else``` ブロックが使える。

```sh
iex> if nil do 
...> "これは通らない"
...> else
...> "これを通す"
...> end
"これを通す"
```
> この言語で、```if/2```や```unldess/2```がマクロとして実装されていることは興味深いらしい。[kernelモジュールのドキュメント](http://elixir-lang.org/docs/stable/elixir/#!Kernel.html)で、もう少し詳しい内容を調べられるようなので、気力があるひとは見ると良さそう。kernelモジュールは、```+/2```演算子や```is_function/2```のような関数が定義されているとのこと。

### do blocks

ここまでで、```case```, ```cond``` , ```if``` , ```unless```を学んだが、全て```do```と```end```のブロックで囲まれていた。
実は```if```を次のようにも書ける

```sh
iex> if true, do: 1 + 2
3
```

```do``` と ```end```のブロッグは、式のまとまりを便利に```do:```へ渡すもの。
下記のものは等価。

```sh
iex> if true do 
...> a = 1 + 2
...> a + 10
...> end
13
iex> if true, do: (
...> a = 1 + 2     
...> a + 10        
...> )             
13
```

私たちは２つ目の構文を**キーワードリスト**を使っていると呼んでいる。 この構文を使って```else``` も渡すことができる。

```sh
iex> if false, do: 2 + 2, else: 1 + 1
2
```

```do``` と ```end``` ブロックを使う際には心に留めておくことがある。ブロックは常に一番外側の関数呼び出しに束縛されるということ。
例えば、次の式。

```sh
iex> if_number if true do
...> 1 + 2
...> end
```

次のように解釈される。```if```が引数1つしか与えられないので、失敗する様子。
GETTING STARTEDでは、```is_number/2```を呼びだそうとして、未定義関数エラーになるとあるが、実際のエラーの内容は、先に```if```の処理がされているのか、ifが未定義関数としてエラーになっている。

```sh
iex> is_number(if true) do
...> 1 + 2
...> end
** (RuntimeError) undefined function: if/1
```

明示的に、括弧を追加してやれば、解決する。

```sh
iex> is_number(if true do
...> 1 + 2
...> end )
true
```

**キーワードリスト**はElixirにおいて、とても重要な役割をになっており、多くの関数やマクロで非常に一般的とのこと。