Исходный код case, cond и if
В этой главе мы изучим управляющие структуры case, cond и if.
case
case позволяет сравнить значение со многими шаблонами до тех пор, пока не будет найден совпадающий:
iex> case {1, 2, 3} do
...> {4, 5, 6} ->
...> "This clause won't match"
...> {1, x, 3} ->
...> "This clause will match and bind x to 2 in this clause"
...> _ ->
...> "This clause would match any value"
...> end
"This clause will match and bind x to 2 in this clause"
Если вы хотите сопоставить шаблон с существующей переменной, вам нужно использовать оператор ^:
iex> x = 1 1 iex> case 10 do ...> ^x -> "Won't match" ...> _ -> "Will match" ...> end "Will match"
В разделах также можно указывать дополнительные условия с помощью охранников:
iex> case {1, 2, 3} do
...> {1, x, 3} when x > 0 ->
...> "Will match"
...> _ ->
...> "Would match, if guard condition were not satisfied"
...> end
"Will match"
Первый раздел будет соответствовать только тогда, когда x является положительным.
Обратите внимание, что ошибки в охранниках не просачиваются, а просто приводят к провалу охранника:
iex> hd(1)
** (ArgumentError) argument error
iex> case 1 do
...> x when hd(x) -> "Won't match"
...> x -> "Got #{x}"
...> end
"Got 1"
Если ни один из разделов не соответствует, генерируется ошибка:
iex> case :ok do ...> :error -> "Won't match" ...> end ** (CaseClauseError) no case clause matching: :ok
В документации модуля Kernel перечислены все доступные охранники в его боковой панели. Вы также можете обратиться к полному справочнику Шаблоны и охранники для углублённой документации.
if
case опирается на сопоставление с шаблонами и охранники, чтобы деструктурировать и сопоставить определенные условия. Однако шаблоны и охранники ограничены только определенными выражениями, которые оптимизируются компилятором. Во многих ситуациях необходимо записать условия, выходящие за рамки того, что можно выразить с помощью case. Для таких случаев if является полезной альтернативой:
iex> if true do ...> "This works!" ...> end "This works!" iex> if false do ...> "This will never be seen" ...> end nil
Если условие, заданное if, возвращает false или nil, тело, заданное между do-end, не выполняется, а вместо этого возвращается nil.
if также поддерживает else блоки:
iex> if nil do ...> "This won't be seen" ...> else ...> "This will" ...> end "This will"
Это также хорошая возможность поговорить о области видимости переменных в Elixir. Если какая-либо переменная объявляется или изменяется внутри if, case и подобных конструкций, объявление и изменение будут видны только внутри конструкции. Например:
iex> x = 1 1 iex> if true do ...> x = x + 1 ...> end 2 iex> x 1
В таких случаях, если вы хотите изменить значение, вы должны вернуть значение из if:
iex> x = 1 1 iex> x = if true do ...> x + 1 ...> else ...> x ...> end 2
if — это макрос
Интересно отметить, что if реализован как макрос в языке: это не специальная конструкция языка, как это было бы во многих языках. Вы можете проверить документацию и её исходный код для получения дополнительной информации.
Если вы обнаруживаете, что вложены несколько блоков if, вы можете рассмотреть использование cond вместо этого. Давайте посмотрим.
cond
Мы использовали case для поиска соответствующих разделов из множества шаблонов. Мы использовали if для проверки одного условия. Если вам нужно проверить несколько условий и найти первое, которое не оценивается как nil или false, cond — полезная конструкция:
iex> cond do ...> 2 + 2 == 5 -> ...> "This will not be true" ...> 2 * 2 == 3 -> ...> "Nor this" ...> 1 + 1 == 2 -> ...> "But this will" ...> end "But this will"
Это эквивалентно else if разделам во многих императивных языках — хотя используется реже в Elixir.
Если все условия возвращают nil или false, возникает ошибка (CondClauseError). По этой причине может потребоваться добавить конечное условие, равное true, которое всегда будет соответствовать:
iex> cond do ...> 2 + 2 == 5 -> ...> "This is never true" ...> 2 * 2 == 3 -> ...> "Nor this" ...> true -> ...> "This is always true (equivalent to else)" ...> end "This is always true (equivalent to else)"
Аналогично if, cond рассматривает любое значение, кроме nil и false, как истинное:
iex> cond do ...> hd([1, 2, 3]) -> ...> "1 is considered as true" ...> end "1 is considered as true"
Подведение итогов
Мы завершили введение в основные управляющие конструкции в Elixir. Как правило, разработчики Elixir предпочитают сопоставление с шаблонами и охранники, используя case и определения функций (которые мы рассмотрим в будущих главах), поскольку они лаконичны и точны. Когда ваша логика не может быть изложена в рамках шаблонов и охранников, вы можете рассмотреть if, переходя к cond, когда есть несколько условий для проверки.
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.18.1/case-cond-and-if.html