Используйте кортеж в качестве аргументов для функции в Elixir

Я пишу игровой движок на Elixir. (Да, я знаю, что это не тот язык, который по своей сути подходит для этого - главное - посмотреть, как использование нетипичного языка влияет на структуру результата.)

Таким образом, у меня есть несколько супервайзеров, которых нужно запустить в начале игры, но что именно они должны контролировать, зависит от игры. Моя мысль состояла в том, чтобы пользователь перечислил необходимые дочерние элементы, а также аргументы и параметры в config.exs файл в виде списка кортежей, и тогда сам супервизор просто извлечет эти кортежи из среды приложения и использует их содержимое в качестве аргументов для worker\2 (или же worker\3при необходимости).

Однако я не могу найти никакого Elixir, эквивалентного распаковке кортежей Python. Я мог бы сделать это сам для этого конкретного случая с простой функцией:

def unpack_worker({module, args}) do
  worker(module, args)
end

def unpack_worker({module, args, opts}) do
  worker(module, args, opts)
end

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

3 ответа

Решение

Я верю, что вы ищете Tuple.to_list/1 а также apply/3:

С их помощью вы можете вызывать функцию правильной арности на основе содержимого кортежа:

def unpack_worker(args) do
  apply(__MODULE__, :worker, Tuple.to_list(args))
end

Если вы сейчас позвоните unpack_worker({})позвоню worker(), unpack_worker({:foo}) позвоню worker(:foo), и так далее.

Демо-версия:

defmodule A do
  def worker, do: IO.puts 0
  def worker(_), do: IO.puts 1
  def worker(_, _), do: IO.puts 2
  def worker(_, _, _), do: IO.puts 3

  def unpack_worker(tuple), do: apply(__MODULE__, :worker, Tuple.to_list(tuple))
end

A.unpack_worker({})
A.unpack_worker({:a})
A.unpack_worker({:a, :b})
A.unpack_worker({:a, :b, :c})

Выход:

0
1
2
3

Я думаю, вам нужно знать размер кортежа, чтобы правильно получить доступ к элементам. Может быть, лучшей структурой данных для вашего варианта использования будет список ключевых слов, особенно если учесть, что это то, что вы получаете из конфигурации в любом случае? Затем вы можете распаковать worker:, args: и оставить остаток как опции?

Я верю, что вы захотите сочетание elem/2 а также tuple_size/1 функции.

iex(1)> a = {:foo, :bar, :baz}
{:foo, :bar, :baz}
iex(2)> tuple_size(a)
3
iex(3)> elem(a, 0)
:foo
iex(4)> elem(a, 5)
** (ArgumentError) argument error
    :erlang.element(6, {:foo, :bar, :baz})

Просто отметьте, что если вы запрашиваете элемент с индексом, который не существует в кортеже, вы получаете ошибку аргумента. Это означает, что вам все еще придется использовать if / case / cond / несколько функциональных головок или что-то, чтобы дифференцировать то, что вы пытаетесь сделать.

Другие вопросы по тегам