Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

制御構文

5 Control syntax.livemd

制御構文

制御構文

Elixirでも手続き型言語と同様、条件分岐(if, cond, case)や例外処理(try~rescue)のような制御構文がある。

ただし、実際にはパターンマッチという強力な仕組みとcaseを組み合わせることで分岐や例外処理を表現することが多い。

case

Elixirではcase、そしてifcondはパターンマッチを用いる関数として実装されている。

よって、「条件によって制御を変える」のではなく「引数の値やパターンによって処理を変える」と解釈するのがいい。

パターンマッチを行うことから、caseの各マッチにおいて変数に値を束縛することができる。

some_list = [0, 1, 2, 3]

case some_list do
  [] -> "empty!"
  [head | _tail] -> "the first value: #{head}"
end
some_map = %{
  a: 0
}

case some_map do
  %{a: value} -> {:ok, "a is #{value}"}
  %{b: value} -> {:ok, "b is #{value}"}
  _ -> {:error, "No expected key"}
end

練習問題

HTTPレスポンスを表すmapがあり、整数のstatusとmapのbodyを含むとする。

case構文を使って、HTTPレスポンスを表すmapのパターンに応じて処理を以下のように分けてみよう。

  • statusが200のとき、:okbodyの組のtupleを作る
  • statusが400のとき、:error:bad_resuestの組のtupleを作る
  • statusが403のとき、:error:not_foundの組のtupleを作る
  • statusが500のとき、:error:internal_errorの組のtupleを作る
# Exercise 5-1
response = %{status: 400, body: %{message: "Invalid parameter!"}}

result =
  case response do
    # implement me!
  end

result == {:error, :bad_request}

マッチの際にwhenによるguard構文を用いることで、さらに細かいパターンマッチを行うことができる。

ただし、guardに用いる条件はコンパイル時に決まっている必要があり、マクロのみが使用可能。
(つまり任意の式や関数を用いることができるわけではない)

some_list = [0, 1, 2, 3]

case some_list do
  [] -> "empty!"
  [head | _] when is_number(head) -> "#{head} is a number"
  [head | _] when is_atom(head) -> "#{head} is an atom"
  [head | _] -> "#{head} is unexpected type"
end

case 式は入れ子にすることもできる。

some_map = %{
  a: %{
    answer: 42
  }
}

case some_map do
  %{b: inner} ->
    inner

  %{a: inner} ->
    case inner do
      %{hello: world} -> world
      %{answer: answer} -> answer
    end
end

ifとcond

Elixirでは基本的に、ifは結果がbooleanとなる式と共に用いる。

condifの条件が複数ある場合に用いる構文である。

ほとんどの場合、パターンマッチとcase構文で事足りるはずなので、ここではあえて取り上げない。