今日のElixirSchoolメモ5「制御構文」

Elixir School「制御構文」の学習メモ

https://elixirschool.com/ja/lessons/basics/control_structures

## `if/2`と`unless/2`

Elixirで偽とみなされる値は`nil`と真偽値の`false`だけ。
`0`や`''`もtrueとみなされる。

```elixir
iex(70)> if true do "true" else "false" end
"true"
iex(71)> if false do "true" else "false" end
"false"
iex(72)> if nil do "true" else "false" end
"false"
iex(73)> if 1 do "true" else "false" end
"true"
iex(74)> if 0 do "true" else "false" end
"true"
iex(75)> if '' do "true" else "false" end
"true"
```

if〜do〜else〜endの書き方は関数の定義と同じように扱うためのシンタックスシュガーで、下記のようにも書ける。
```elixir
if String.valid?("Hello"), do: "Valid string!", else: "Invalid string."
```

```elixir
if(String.valid?("Hello"), [{:do, "Valid string!"}, {:else, "Invalid string."}])
```

## `case/2`の構文

複数のパターンに対してマッチする必要があるときに使う。
`->` は他言語だと`then`にあたる。

```elixir
iex(77)> case {:ok, "Hello, world"} do
...(77)>   {:ok, result} -> result
...(77)>   {:error} -> "Uh oh!"
...(77)>   _ -> "Catch all"
...(77)> end
"Hello, world"
```

`_`は他のすべてにマッチするelseに該当する。
マッチするものが見当たらない場合、`CaseClauseError`が発生する。

```elixir
iex(78)> case :even do
...(78)>   :odd -> "Odd"
...(78)> end
** (CaseClauseError) no case clause matching: :even
```

### `case/2`

`case/2`はパターンマッチングに依存しているので、パターンマッチングと同じルールや制限がすべて適応される。

```elixir
iex(78)> pie = 3.14
3.14
iex(79)> case "cherry pie" do
...(79)>   ^pie -> "not so taisty"
...(79)>   pie -> "i bet #{pie} is tasty"
...(79)> end
"i bet cherry pie is tasty"
```

```elixir
iex(80)> case 3.14 do
...(80)>   ^pie -> "not so taisty"
...(80)>   pie -> "i bet #{pie} is tasty"
...(80)> end
"not so taisty"
```

### `case/2`はガード節に対応している。

```elixir
iex(83)> case {1, 2, 3} do
...(83)>   {1, x, 3} when x > 0 ->
...(83)>     "will match"
...(83)>   _ ->
...(83)>     "won't match"
...(83)> end
"will match"
```

```elixir
iex(84)> case {1, 0, 3} do
...(84)>   {1, x, 3} when x > 0 ->
...(84)>     "will match"
...(84)>   _ ->
...(84)>     "won't match"
...(84)> end
"won't match"
```

## `cond/1`

`cond/1`は他の言語でいうところの`else if`や`elsif`のようなもの。
値ではなく条件をマッチさせる必要があるときに使う。

```elixir
iex(86)> cond do
...(86)>   2 + 2 == 5 -> "A"
...(86)>   2 * 2 == 3 -> "B"
...(86)>   1 + 1 == 2 -> "C"
...(86)> end
"C"
```

`case`のように`cond`はマッチしない場合にエラーを発生させる。
それに対応するには`true`になる条件を定義する。

```elixir
iex(87)> cond do
...(87)>   7 + 1 == 0 -> "fuga"
...(87)>   true -> "catch all"
...(87)> end
"catch all"
```

## `with`

with式には2つの使い方がある。
まずは、ローカル変数のスコープを定義すること。計算中に一時的な変数が必要になって、その変数が外に漏れてほしくないとき。
2つ目の役割はパターンマッチングの失敗を制御したいとき。

### 2つ目の例

```elixir
iex(90)> user = %{first: "Sean", last: "Callan"}
%{first: "Sean", last: "Callan"}
iex(92)> with {:ok, first} <- Map.fetch(user, :first),
...(92)>      {:ok, last} <- Map.fetch(user, :last),
...(92)>      do: last <> ", " <> first
"Callan, Sean"
```

### `with/1`はElixir1.3からelseを使える。

```elixir
iex(97)> import Integer
Integer
iex(98)> m = %{a: 1, c: 3}
%{a: 1, c: 3}
iex(99)> a =
...(99)>   with {:ok, number} <- Map.fetch(m, :a),
...(99)>     true <- is_even(number) do
...(99)>       IO.puts "#{number} divided by 2 is #{div(number, 2)}"
...(99)>       :even
...(99)>   else
...(99)>     :error ->
...(99)>       IO.puts("We don't have this item in map")
...(99)>       :error
...(99)>
...(99)>     _ ->
...(99)>       IO.puts("It is odd")
...(99)>       :odd
...(99)>   end
It is odd
:odd
```