loading...
gumi TECH Blog

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

gumitech profile image gumi TECH Updated on ・3 min read

本稿は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"

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

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

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

iex> x = 1
1
iex> case 2 do
...>   ^x -> 1
...>   x -> x
...> end
2
iex> x
1

処理の中にさらに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

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

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

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

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

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

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

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

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

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

ifとunless

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

iex> if true do
...>   "実行される"
...> end
"実行される"

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

iex> if !0 do
...>   true
...> else
...>   false
...> end
false

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

iex> unless 0 do
...>   "実行されない"
...> end
nil

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

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

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

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

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

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

iex> if true do
...>   x = 2
...>   x * x
...> end
4

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

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

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

iex> is_number(
...> if true do
...>   x = 2
...> end
...> )
true

Elixir入門もくじ

番外

Posted on by:

gumitech profile

gumi TECH

@gumitech

gumi TECH は、株式会社gumiのエンジニアによる技術記事公開やDrinkupイベントなどの技術者交流を行うアカウントです。 gumi TECH Blog: http://dev.to/gumi / gumi TECH Drinkup: http://gumitech.connpass.com

gumi TECH Blog

株式会社gumiのエンジニアによる技術記事を公開しています。

Discussion

pic
Editor guide