В Эликсир одна функция может иметь несколько тел -- несколько разных блоков кода. В зависимости от входящих аргументов выполняется только один из этих блоков.
По английски термин "тело функции" пишется clause и произносится [klôz]
. Поскольку это короче, то все Эликсир разработчики предпочитают говорить "клоз" вместо "тело функции".
def handle({:dog, name}, :add) do
IO.puts("add dog #{name}")
end
def handle({:dog, name}, :remove) do
IO.puts("remove dog #{name}")
end
def handle({:cat, name}, :add) do
IO.puts("add cat #{name}")
end
def handle({:cat, name}, :remove) do
IO.puts("remove cat #{name}")
end
Здесь функция handle/2
имеет 4 тела. Шаблоны описываются прямо в аргументах функции, отдельно для каждого тела. Принцип такой же, как и с конструкцией case -- шаблоны проверяются по очереди на совпадение с входящими аргументами функции. Первый совпавший шаблон вызывает соответствующий блок кода и останавливает дальнейший перебор. Если ни один шаблон не совпал, то генерируется исключение.
Как и в случае с case, здесь тоже важна очередность шаблонов. Типичная ошибка -- расположить более общий шаблон выше, чем более специфичный шаблон:
def handle(animal, action) do
IO.puts("do something")
end
def handle({:dog, name}, :add) do
IO.puts("add dog #{name}")
end
Во многих таких случаях компилятор выдаст предупреждение:
warning: this clause for handle/2 cannot match because a previous clause at line 27 always matches
Но бывает, что компилятор не замечает проблему.
Как и с case, с телами функций могут использоваться охранные выражения:
def handle({:dog, name, age}) when age > 10 do
IO.puts("#{name} is a dog older than 10")
end
def handle({:dog, name, _age}) do
IO.puts("#{name} is a 10 years old or younger dog")
end
def handle({:cat, name, age}) when age > 10 do
IO.puts("#{name} is a cat older than 10")
end
def handle({:cat, name, _age}) do
IO.puts("#{name} is a 10 years old or younger cat")
end
Конструкция case и тела функций полностью эквивалентны друг другу. Выбор того или иного варианта является личным предпочтением разработчика.
Поиграем в "крестики-нолики". Игровая доска размером 3х3 ячеек представлена кортежем из 3-х кортежей:
{
{:x, :o, :f},
{:f, :o, :f},
{:f, :f, :f}
}
Каждая ячейка может находиться в одном из 3-х состояний:
:x
в ячейке стоит крестик;:o
в ячейке стоит нолик;:f
ячейка свободна.Реализовать функцию valid_game?(state)
, которая получает на вход состояние игры, и проверяет, является ли это состояние валидным. То есть, имеет ли состояние правильную структуру, и заполнены ли ячейки только валидными значениями. Функция возвращает булевое значение.
Реализовать функцию check_who_win(state)
, которая получает состояние и возвращает победителя, если он есть. Функция должна определить наличие трех крестиков или трех ноликов по горизонтали, или по вертикали, или по диагонали. В случае победы крестиков функция возвращает {:win, :x}
, в случае победы ноликов функция возвращает {:win, :o}
, если победы нет, функция возвращает :no_win
.
Solution.valid_game?({{:x, :x, :x}, {:x, :x, :x}, {:x, :x, :x}})
# => true
Solution.valid_game?({{:f, :f, :f}, {:f, :f, :f}, {:f, :f, :f}})
# => true
Solution.valid_game?({{:x, :o, :some}, {:f, :x, :o}, {:o, :o, :x}})
# => false
Solution.valid_game?({{:x, :o, :f}, {:f, :x, :x, :o}, {:o, :o, :x}})
# => false
Solution.check_who_win({{:x, :x, :x}, {:f, :f, :o}, {:f, :f, :o}})
# => {:win, :x}
Solution.check_who_win({{:o, :x, :f}, {:o, :f, :x}, {:o, :f, :f}})
# => {:win, :o}
Solution.check_who_win({{:x, :f, :f}, {:f, :x, :x}, {:f, :f, :o}})
# => :no_win
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Ваше упражнение проверяется по этим тестам
1defmodule Test do
2 use ExUnit.Case
3 import Solution
4
5 test "valid_game? positive test" do
6 assert valid_game?({{:x, :x, :x}, {:x, :x, :x}, {:x, :x, :x}})
7 assert valid_game?({{:o, :o, :o}, {:o, :o, :o}, {:o, :o, :o}})
8 assert valid_game?({{:f, :f, :f}, {:f, :f, :f}, {:f, :f, :f}})
9 assert valid_game?({{:x, :o, :f}, {:f, :x, :o}, {:o, :o, :x}})
10 end
11
12 test "valid_game? negative test" do
13 assert not valid_game?({{:x, :o, :some}, {:f, :x, :o}, {:o, :o, :x}})
14 assert not valid_game?({{:x, :x, :x}, {:x, :some, :x}, {:x, :x, :x}})
15 assert not valid_game?({{:o, :o, :o}, {:o, :o, :o}, {:o, :o, :some}})
16
17 assert not valid_game?({{:x, :o, :f}, {:f, :x, :o}, {:o, :o, :x, :x}})
18 assert not valid_game?({{:x, :o, :f}, {:f, :x, :x, :o}, {:o, :o, :x}})
19 assert not valid_game?({{:x, :o, :x, :f}, {:f, :x, :o}, {:o, :o, :x}})
20
21 assert not valid_game?({{:x, :o, :f}, {:f, :x, :o}, {:o, :o}})
22 assert not valid_game?({{:x, :o, :f}, {:f, :o}, {:o, :o, :x}})
23 assert not valid_game?({{:x, :o}, {:f, :x, :o}, {:o, :o, :x}})
24
25 assert not valid_game?({{:x, :o, :f}, {:f, :x, :o}, {:o, :o, :x}, {:x, :x, :x}})
26 assert not valid_game?({{:x, :o, :f}, {:f, :x, :o}})
27 end
28
29 test "check_who_win test" do
30 assert {:win, :x} == check_who_win({{:x, :x, :x}, {:f, :f, :o}, {:f, :f, :o}})
31 assert {:win, :o} == check_who_win({{:f, :x, :f}, {:o, :o, :o}, {:x, :f, :f}})
32 assert {:win, :x} == check_who_win({{:f, :o, :f}, {:o, :f, :f}, {:x, :x, :x}})
33
34 assert {:win, :o} == check_who_win({{:o, :x, :f}, {:o, :f, :x}, {:o, :f, :f}})
35 assert {:win, :x} == check_who_win({{:f, :x, :o}, {:p, :x, :f}, {:f, :x, :f}})
36 assert {:win, :o} == check_who_win({{:f, :x, :o}, {:f, :x, :o}, {:f, :f, :o}})
37
38 assert {:win, :x} == check_who_win({{:x, :f, :o}, {:o, :x, :f}, {:f, :f, :x}})
39 assert {:win, :o} == check_who_win({{:f, :f, :o}, {:x, :o, :f}, {:o, :x, :f}})
40
41 assert :no_win == check_who_win({{:x, :f, :f}, {:f, :x, :x}, {:f, :f, :o}})
42 assert :no_win == check_who_win({{:x, :o, :o}, {:o, :x, :x}, {:x, :o, :o}})
43 end
44end
45
Решение учителя откроется через: