DEV Community

gumi TECH for gumi TECH Blog

Posted on • Updated on

Elixir入門 05: 条件 - case/cond/if

本稿はElixir公式サイトの許諾を得て「case, cond, and if」の解説にもとづき、加筆補正を加えてElixirの条件に応じた処理についてご説明します。

case

case/2endで閉じ、その中に条件に応じた処理をいくつでも書き加えられます。条件に合うかどうか決めるのは、パターンマッチングです。残るすべての場合を引き受けるには_を用います。

iex> case {:ok, "hello world"} do
...>   {:ok, result} -> result
...>   {:error, error} -> error
...>   _ -> "others"
...> end
"hello world"
Enter fullscreen mode Exit fullscreen mode

条件にマッチする処理が書かれていないとエラーになります。

iex> case rem(1, 2) do
...>   0 -> :even
...> end
** (CaseClauseError) no case clause matching: 1
Enter fullscreen mode Exit fullscreen mode
iex> case rem(1, 2) do
...>   0 -> :even
...>   1 -> :odd
...>   _ -> :oddity
...> end
:odd
Enter fullscreen mode Exit fullscreen mode

case/2の外の変数をパターンとして参照するときは、^/1演算子を添えなければなりません。中で定められる変数とは別の扱いになるからです。

iex> x = 1
1
iex> case 2 do
...>   ^x -> 1
...>   x -> x
...> end
2
iex> x
1
Enter fullscreen mode Exit fullscreen mode

処理の中にさらにwhenキーワードで条件(ガード)を加えることができます(「Guards」参照)。

iex> case 3 do
...>   1 -> :one
...>   2 -> :two
...>   n when is_integer(n) and n > 2 -> :larger_than_two
...>   _ -> :not_integer
...> end
:larger_than_two
Enter fullscreen mode Exit fullscreen mode

無名関数の引数についても、ガードを定めて処理を場合分けできます。

iex> is_even? = fn
...>   n when rem(n, 2) == 0 -> true
...>   n when rem(n, 2) == 1 -> false
...>   _ -> :not_integer
...> end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex> is_even?.(1)
false
iex> is_even?.(2)
true
iex> is_even?.(2.0)
:not_integer
Enter fullscreen mode Exit fullscreen mode

rem/2関数の引数は整数でなければなりません。けれど、上のコードでガードの式はエラーを出さず、ただパターンにマッチしないと扱われていることにご注意ください。

iex> rem(2.0, 2)
** (ArithmeticError) bad argument in arithmetic expression
    :erlang.rem(2.0, 2)
Enter fullscreen mode Exit fullscreen mode

なお、無名関数の処理を引数によって場合分けしたとき、アリティは同じでなければなりません。

iex> case func = fn
...>   x -> x
...>   x, y -> x + y
...> end
** (CompileError) iex:14: undefined function case/1
Enter fullscreen mode Exit fullscreen mode

cond

case/2はひとつの値をいくつかのパターンからマッチさせて処理するのに適しています。けれども、条件がさまざまな場合もあるでしょう。そのときに用いるのがcond/1です。条件は論理値として評価されます。falsenil以外はtrueです。はじめにtrueと評価された処理が行われます。

iex> cond do
...>   1 > 2 -> false
...>   is_integer(1.0) -> false
...>   nil -> false
...>   [1, 2, 3] -> true
...>   true -> "実行されない"
...> end
true
Enter fullscreen mode Exit fullscreen mode

trueと評価される条件がないとエラーになります。そこで、最後に条件がtrueの処理を加えておくとよいでしょう。

iex> cond do
...>   1 == 2 -> false
...> end
** (CondClauseError) no cond clause evaluated to a true value
Enter fullscreen mode Exit fullscreen mode

ifとunless

評価する条件がひとつだけのときは、if/2unless/2が使えます。これらは言語そのものに備わる機能ではありません。Elixirのマクロとして定められています。

iex> if true do
...>   "実行される"
...> end
"実行される"
Enter fullscreen mode Exit fullscreen mode

条件がtrueと評価されない場合(false)の処理はelseブロックで加えられます。

iex> if !0 do
...>   true
...> else
...>   false
...> end
false
Enter fullscreen mode Exit fullscreen mode

unless/2if/2と反対に、条件がfalseと評価されるときに処理を行います。

iex> unless 0 do
...>   "実行されない"
...> end
nil
Enter fullscreen mode Exit fullscreen mode

do/endブロック

ご紹介した4つの条件構文case/2cond/1if/2unless/2は、いずれも処理をdo/endブロックに書きました。このブロックを使わずにひとつの式で書くこともできます。たとえば、if/2の場合です。

iex> if(0 < 1, do: 1 + 2)
3
iex> if(false, do: :this, else: :that)
:that
Enter fullscreen mode Exit fullscreen mode

if/2のアリティは2でした。あとの例は引数を3つとっているように見えます。Elixirはdoelseは、それぞれをキーワードとしたひとつのリストと捉えているのです。

iex> if(false, [do: :this, else: :that])
:that
Enter fullscreen mode Exit fullscreen mode

複数の処理は改行で区切ります。

iex(28)> if(true, do: (
...(28)>   x = 2
...(28)>   x * x
...(28)> ))
4
Enter fullscreen mode Exit fullscreen mode

引数のカンマ区切りとキーワードリストの書き方では、コードが長くなったとき見にくくなります。そこで、do/endブロックが採り入れられたのです。

iex> if true do
...>   x = 2
...>   x * x
...> end
4
Enter fullscreen mode Exit fullscreen mode

条件構文はひとつの式ですから、関数の引数に渡すこともできます。けれども、つぎのコードではエラーになります。これは、構文全体でなく、iftrueのふたつがis_number/1関数の引数と捉えられてしまったからです。

iex> is_number if true do
...>   x = 2
...> end
** (CompileError) iex:32: undefined function is_number/2
Enter fullscreen mode Exit fullscreen mode

構文全体を()でかこんで、関数のひとつの引数であることをはっきりさせれば正しく動きます。

iex> is_number(
...> if true do
...>   x = 2
...> end
...> )
true
Enter fullscreen mode Exit fullscreen mode

Elixir入門もくじ

番外

Discussion (0)