case, cond, if, unless

 cf. 5 case,condそしてif – case, cond and if – Elixir

case

case は1つの値をいずれかの句(clause)にマッチするまで、複数の句と比較する。

iex(1)> case {1, 2, 3} do
...(1)>   {4, 5, 6} -> "This clause won't match"
...(1)>   {1, x, 3} -> "This clause will match and bind x to 2 in this clause"
...(1)>   _ -> "This clause would match any value"
...(1)> end
"This clause will match and bind x to 2 in this clause"

上の例では、2番目の句がマッチして、変数 x に値 2 が束縛されている。この変数 x は句の中だけで有効らしい。
もし、すでにある変数にマッチさせたい場合は ^ (ピン演算子)を使う。

iex(2)> x = 1
1
iex(3)> case 10 do
...(3)>   ^x -> "Won't match"
...(3)>   _  -> "Will match"
...(3)> end
"Will match"

句にはガード(guard)をつけることで、条件を追加することができる。

iex(4)> case {1, 2, 3} do
...(4)>   {1, x, 3} when x > 0 -> "Will match"
...(4)>   _ -> "Won't match"
...(4)> end
"Will match"

ガードに使える式には制限があるけど、詳しくはリンク先参照。制限といっても普通に使う分には困らない程度に見える。
ガードで起きたエラーは、単にガードの失敗とみなされて外には影響しない。次の例では、hd(x)x がリストではないのでエラーになってるはずだけど、case 全体としてはエラーにならない。

iex(5)> case 1 do
...(5)>   x when hd(x) -> "Won't match"
...(5)>   x -> "Got: #{x}"
...(5)> end
"Got: 1"

もし、どの句にもマッチしなければエラーになる。

iex(6)> case :ok do
...(6)>   :error -> "Won't match"
...(6)> end
** (CaseClauseError) no case clause matching: :ok

匿名関数

ちょっと話がそれるけど、匿名関数も句やガードを持つことができる。

iex(6)> f = fn
...(6)>   x, y when 0 > x -> x + y
...(6)>   x, y -> x * y
...(6)> end
#Function<12.52032458/2 in :erl_eval.expr/5>
iex(7)> f.(1, 3)
3
iex(8)> f.(-1, 3)
2

cond

Scheme の cond と同じ。

iex(9)> cond do
...(9)>   2 + 2 == 5 -> "This will not be true"
...(9)>   2 * 2 == 3 -> "Nor this"
...(9)>   1 + 1 == 2 -> "But this will"
...(9)> end
"But this will"

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

iex(10)> cond do
...(10)>   2 + 2 == 5 -> "This is never true"
...(10)>   2 * 2 == 3 -> "Nor this"
...(10)> end
** (CondClauseError) no cond clause evaluated to a true value

ifとunless

試したい条件が1つだけの時は、ifunless マクロが使える。……なんか「マクロ」って単語がさらっと出てきたぞ。

iex(10)> if true do
...(10)>   "This works"
...(10)> end
"This works"
iex(11)> unless true do
...(11)>   "This will never be seen"
...(11)> end
nil

else も使える。

iex(12)> if nil do
...(12)>   "This won't be seen"
...(12)> else
...(12)>   "This will"
...(12)> end
"This will"

doブロック

if は次のようにも書ける。

iex(13)> if true, do: 1 + 2
3

Elixir では do ~ end は、式のまとまりを do: へ渡す役割をしている。次の2つの例は同じことをしている。

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

2つ目の構文は「キーワードリストを使っている」と言う。もちろん else も使える。

iex(16)> if false, do: :this, else: :that
:that