Как сделать сопоставление образцов на клавишах карты в головах функций в эликсире

Я не могу найти способ сопоставления шаблонов на ключе карты в голове функции. Есть ли способ сделать это? То, что я пытаюсь сделать, это запустить другой код в зависимости от того, существует ли какой-либо ключ на карте или нет (а также нужно избегать if/else и т.п.)

Вот как выглядит мой код

def my_func(key, %{key => _} = map), do: ...

который дает мне эту ошибку

** (CompileError) незаконное использование ключа переменной внутри соответствия ключевому ключу, карты могут соответствовать только существующей переменной с помощью клавиши

Конечно, я также пробовал его с помощью ^

def my_func(key, %{^key => _} = map), do: ...

который затем дает

** (CompileError) несвязанная переменная ^ ключ

Я использую elixir 1.3.1/erlang 19.0 x64 на машине с Windows 8.1. Спасибо за чтение!

Ответ 1

Просто совпадающий шаблон с ключом, который вам нужен:

defmodule Test do
  def my_func(%{"a" => value}), do: {:a, value}
  def my_func(%{"b" => value}), do: {:b, value}
  def my_func(_), do: :error
end

Затем в IEx:

iex(1)> Test.my_func(%{"a" => 1})
{:a, 1}
iex(2)> Test.my_func(%{"b" => 2})
{:b, 2}

Также важны порядок предложений. Например, если вы пытаетесь сопоставить %{"b" => 2}, но у вас есть следующая карта %{"a" => 1, "b" => 2}, ключ "a" будет соответствовать первым, поскольку находится в первом разделе:

iex(3)> Test.my_func(%{"a" => 1, "b" => 2})
{:a, 1}

Если вы хотите сгенерировать что-то для каждого ключа, который вы можете сопоставить, я рекомендую другой подход. Например, если вы хотите сопоставить функцию с этими клавишами:

defmodule Test0 do
  def my_op({"times_2", value}), do: {"times_2", value * 2}
  def my_op({"times_3", value}), do: {"times_3", value * 3}
  def my_op({key, value}), do: {key, value}

  def my_func(m) do
    Enum.map(m, &my_op/1) |> Enum.into(%{})
  end
end

Итак, вы получите следующее:

iex(1)> Test0.my_func(%{"times_2" => 2, "times_3" => 3, "whatever" => 42})
%{"times_2" => 4, "times_3" => 9, "whatever" => 42}

Обновить в соответствии с комментариями для этого ответа

Вы не можете сопоставить шаблон переменной с переменной. Проблема заключается в том, что компилятор должен генерировать код для поиска того, чего он еще не знает. Когда вы сопоставляете шаблон, вы обычно даете компилятору некоторые намеки на то, что он получит. Для ключей в картах подсказка - это ключ. В этом случае, даже указывая, что первым аргументом должен быть ключ к поиску, его недостаточно для компилятора. Поэтому ваш подход должен заключаться в использовании оператора if:

defmodule Test2 do
  def my_func(k, m) do
    if Map.haskey?(k, m), do: warning(k, m), else: foo(k, m)
  end

  def warning(k, m) do
    #Warning
    m
  end

  # In this function you could apply the pattern matching
  # described in the first part of this answer.
  def foo(k, m) do
    value = # do something
    %{m | key => value}
  end
end

Надеюсь, это ответит на ваш вопрос.