Используйте кортеж в качестве аргументов для функции в 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
/ несколько функциональных головок или что-то, чтобы дифференцировать то, что вы пытаетесь сделать.