Elixir: Сопоставление с образцом для словарей
Есть некоторые нюансы сопоставления с образцом при работе со словарями. В шаблоне не нужно перечислять все ключи, какие есть в словаре. Мы указываем только те ключи, которые нам нужны:
my_map = %{a: 1, b: 2, c: 3}
%{a: value} = my_map
IO.puts(value) # => 1
Если ключи не являются атомами, то синтаксис отличается:
my_map = %{"a" => 1, "b" => 2, "c" => 3}
%{"a" => value1} = my_map
IO.puts(value1) # => 1
%{"b" => value2, "c" => value3} = my_map
IO.puts(value2) # => 2
IO.puts(value3) # => 3
Шаблон %{}
совпадает с любым словарём. Это контринтуитивно, можно было бы ожидать, что этот шаблон совпадает только с пустым словарём. Этим шаблоном нельзя ничего извлечь, но можно проверить, что значение является словарём, а не чем-то иным.
my_map = %{"a" => 1, "b" => 2, "c" => 3}
%{} = my_map
my_map = 42
%{} = my_map # ** (MatchError) no match of right hand side value: 42
Переменные можно использовать для извлечения значений, но не для извлечения ключей:
%{"c" => my_var} = my_map
IO.puts(my_var) # => 3
%{my_var => 1} = my_map # ** (CompileError) iex:17: cannot use variable my_var as map key inside a pattern.
Переменная в шаблоне может выполнять две роли. Либо мы хотим, чтобы эта переменная получила новое значение, и тогда не важно, использовалась ли эта переменная раньше, было ли у нее какое-то значение. Либо мы хотим использовать значение, которое переменная уже имеет, как часть шаблона. Во втором случае понадобится pin operator.
animal = :cat
{^animal, "Tihon"} = {:cat, "Tihon"}
{^animal, "Tihon"} = {:dog, "Tihon"} # ** (MatchError) no match of right hand side value: {:dog, "Tihon"}
pin operator извлекает текущее значение переменной и подставляет его в шаблон. И дальше это значение в шаблоне работает как литерал.
pin operator можно использовать и для ключа, и для значения:
value1 = 1
%{"a" => ^value1} = my_map
keyb = "b"
%{^keyb => _} = my_map
Задание
Реализовать функцию get_values(data)
, которая принимает словарь, извлекает из него два значения по ключам :a
и :b
, и возвращает их в виде кортежа {a_value, b_value}
.
Реализовать функцию get_value_by_key(data, key)
, которая принимает словарь и ключ, извлекает значение по указанному ключу и возвращает его.
Обе функции генерируют исключение MatchError
если в словаре нет нужных ключей.
Solution.get_values(%{a: 1, b: 2})
# => {1, 2}
Solution.get_values(%{a: :ok, b: 42, c: true})
# => {:ok, 42}
Solution.get_values(%{})
# => MatchError
Solution.get_value_by_key(%{answer: 42}, :answer)
# => 42
Solution.get_value_by_key(%{question: "6 * 7"}, :question)
# => "6 * 7"
Solution.get_value_by_key(%{a: 1}, :b)
# => MatchError
Упражнение не проходит проверку — что делать? 😶
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
- Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет 🤨
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Мой код отличается от решения учителя 🤔
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Прочитал урок — ничего не понятно 🙄
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.