如何通过指定模块和方法名称,在Elixir中动态调用方法?

47

我想知道 Elixir 中方法名的确切含义:

array = [1,2,3]
module_name = :lists
method_name = :nth                  # this not working
module_name.method_name(1, array)   # error, undef function lists.method_name/2
module_name.nth(1, array)           # returns 1, module_name is OK. It's an atom

但我几乎可以在Erlang中做同样的事情:

A = [1,2,3].
X = lists.
Y = nth.
X:Y(1,A).  #  returns 1

我该如何在 Elixir 中实现这一操作?

1个回答

67
您可以使用apply/3,它只是:erlang.apply/3的包装器。它简单地从给定的module中调用function,并传递一个arguments数组。由于您将模块和函数名称作为参数传递,因此可以使用变量。
apply(:lists, :nth, [1, [1,2,3]])
apply(module_name, method_name, [1, array])

如果你想更好地理解Elixir如何处理函数调用(以及其他所有内容),你应该查看quoteunquote

contents = quote do: unquote(module_name).unquote(method_name)(1, unquote(array))

该函数返回函数调用的同构表示形式。
{{:.,0,[:lists,:nth]},0,[1,[1,2,3]]}

你可以使用 Code.eval_quoted/3 函数来取消引用的函数调用。
{value, binding} = Code.eval_quoted(contents)

编辑:这里有一个使用Enum.fetch和var的示例。

quoted_fetch = quote do: Enum.fetch([1,2,3], var!(item));             
{value, binding} = Code.eval_quoted(quoted_fetch, [item: 2])

好的。所以方法名是一个原子。现在我认为只是语法不允许我们在Elixir中编写module.method,对吗? - halfelf
1
我相信你是正确的。我认为使这个工作的唯一方法就是改变语法,使用原子来调用模块函数(例如 :lists.:nth)。在这种情况下,我宁愿使用 apply。 - lastcanal
你能展示一下如何对普通的Elixir函数进行操作吗?比如Enum.fetch?是否有变量变量或变量函数? - CMCDragonkai
1
quoted_fetch = quote do: Enum.fetch([1,2,3], var!(item)); {value, binding} = Code.eval_quoted(quoted_fetch, [item: 2]) 引用获取 = 引用 do: Enum.fetch([1,2,3], var!(item)); {value, binding} = Code.eval_quoted(引用获取, [item: 2]) - lastcanal
我已经更新了答案以获得更好的格式。使用var!/1允许您在引用方法中替换变量,以获取绑定中相应键的值。 - lastcanal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接