Elixir入門 05: 条件 - case/cond/if
Tags: elixir, webdev, tutorial, programming
Published: 2018-10-16 01:01:27

---
title: Elixir入門 05: 条件 - case/cond/if
published: true
description: Elixir公式サイトの許諾を得て「case, cond, and if」の解説にもとづき、加筆補正を加えてElixirの条件に応じた処理についてご説明します。
tags: #elixir, #webdev, #tutorial, #programming
---

本稿はElixir公式サイトの許諾を得て「[case, cond, and if](https://elixir-lang.org/getting-started/case-cond-and-if.html)」の解説にもとづき、加筆補正を加えてElixirの条件に応じた処理についてご説明します。


# case
[case/2](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#case/2)は`end`で閉じ、その中に条件に応じた処理をいくつでも書き加えられます。条件に合うかどうか決めるのは、パターンマッチングです。残るすべての場合を引き受けるには`_`を用います。

```elixir
iex> case {:ok, "hello world"} do
...>   {:ok, result} -> result
...>   {:error, error} -> error
...>   _ -> "others"
...> end
"hello world"
```
条件にマッチする処理が書かれていないとエラーになります。

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

```elixir
iex> case rem(1, 2) do
...>   0 -> :even
...>   1 -> :odd
...>   _ -> :oddity
...> end
:odd
```

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

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

処理の中にさらに`when`キーワードで条件(ガード)を加えることができます(「[Guards](https://hexdocs.pm/elixir/guards.html)」参照)。

```elixir
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
```
無名関数の引数についても、ガードを定めて処理を場合分けできます。

```elixir
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`関数](https://hexdocs.pm/elixir/Kernel.html#rem/2)の引数は整数でなければなりません。けれど、上のコードでガードの式はエラーを出さず、ただパターンにマッチしないと扱われていることにご注意ください。

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

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

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


# cond
`case/2`はひとつの値をいくつかのパターンからマッチさせて処理するのに適しています。けれども、条件がさまざまな場合もあるでしょう。そのときに用いるのが[`cond/1`](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#cond/1)です。条件は論理値として評価されます。`false`と`nil`以外は`true`です。はじめに`true`と評価された処理が行われます。

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

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

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


#ifとunless
評価する条件がひとつだけのときは、[`if/2`](https://hexdocs.pm/elixir/Kernel.html#if/2)と[`unless/2`](https://hexdocs.pm/elixir/Kernel.html#unless/2)が使えます。これらは言語そのものに備わる機能ではありません。Elixirのマクロとして定められています。


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

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

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

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

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


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

```elixir
iex> if(0 < 1, do: 1 + 2)
3
iex> if(false, do: :this, else: :that)
:that
```

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

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

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

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

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

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

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

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

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

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

#### Elixir入門もくじ
- [Elixir入門 01: コードを書いて試してみる](https://dev.to/gumi/elixir-01--2585)
- [Elixir入門 02: 型の基本](https://dev.to/gumi/elixir-02--30n1)
- [Elixir入門 03: 演算子の基本](https://dev.to/gumi/elixir-03--33im)
- [Elixir入門 04: パターンマッチング](https://dev.to/gumi/elixir-04--1346)
- Elixir入門 05: 条件 - case/cond/if
- [Elixir入門 06: バイナリと文字列および文字リスト](https://dev.to/gumi/elixir-06--35na)
- [Elixir入門 07: キーワードリストとマップ](https://dev.to/gumi/elixir-07--39hi)
- [Elixir入門 08: モジュールと関数](https://dev.to/gumi/elixir-08--1c4c)
- [Elixir入門 09: 再帰](https://dev.to/gumi/elixir-09--1a0p)
- [Elixir入門 10: EnumとStream](https://dev.to/gumi/elixir-10-enumstream-4fpb)
- [Elixir入門 11: プロセス](https://dev.to/gumi/elixir-11--2mia)
- [Elixir入門 12: 入出力とファイルシステム](https://dev.to/gumi/elixir-12--4og6)
- [Elixir入門 13: aliasとrequireおよびimport](https://dev.to/gumi/elixir-13-aliasrequireimport-55c1)
- [Elixir入門 14: モジュールの属性](https://dev.to/gumi/elixir-14--3511)
- [Elixir入門 15: 構造体](https://dev.to/gumi/elixir-15--4f43)
- [Elixir入門 16: プロトコル](https://dev.to/gumi/elixir-16--lif)
- [Elixir入門 17: 内包表記](https://dev.to/gumi/elixir-17--5gci)
- [Elixir入門 18: シギル](https://dev.to/gumi/elixir-18--5791)
- [Elixir入門 19: tryとcatchおよびrescue](https://dev.to/gumi/elixir-19-trycatchrescue-50i8)
- [Elixir入門 20: 型の仕様とビヘイビア](https://dev.to/gumi/elixir-20--j50)
- [Elixir入門 21: デバッグ](https://dev.to/gumi/elixir-21--21a1)
- [Elixir入門 22: Erlangライブラリ](https://dev.to/gumi/elixir-22-erlang-2492)
- [Elixir入門 23: つぎのステップ](https://dev.to/gumi/elixir-23--50ik)

##### 番外
- [Elixir入門: Plugを使うには](https://dev.to/gumi/elixir-plug-40lb)