Источник 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/unless
case опирается на сопоставление шаблонов и защит для деструктуризации и сопоставления с определёнными условиями. Однако шаблоны и защиты ограничены только определёнными выражениями, которые оптимизируются компилятором. В многих ситуациях вам нужно написать условия, выходящие за рамки того, что можно выразить с case. Для этого if/2 (и unless/2) являются полезными альтернативами:
iex> if true do ...> "This works!" ...> end "This works!" iex> unless true do ...> "This will never be seen" ...> end nil
Если условие, заданное для if/2, возвращает false или nil, тело, заданное между do-end, не выполняется, а вместо этого возвращается nil. Обратное происходит с unless/2.
Они также поддерживают else блоки (хотя использование else с unless обычно не рекомендуется):
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иunless— макросыИнтересно отметить, что
if/2иunless/2реализованы как макросы в языке: они не являются специальными конструкциями языка, как в многих других языках. Вы можете проверить документацию и их исходный код для получения дополнительной информации.
Если вы обнаружите себя вложенными несколькими блоками if/2, возможно, стоит рассмотреть использование cond/1 вместо этого. Давайте посмотрим.
cond
Если вам нужно проверить несколько условий и найти первое, которое не оценивается как nil или false, cond/1 является полезной конструкцией:
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/2 и unless/2, 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/2, возвращаясь к cond/1, когда нужно проверить несколько условий.
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/case-cond-and-if.html