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

问题描述:

我想知道确切的方法名是什么仙丹:如何通过指定模块和方法名称在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 

但我几乎可以做同样的事情在二郎:

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

我怎样才能做到这一点的灵药?

您可以使用apply/3这仅仅是围绕:erlang.apply/3的包装。它只是invokes the given function from the module with an array of arguments.由于您将参数作为模块和函数名称传递,因此可以使用变量。

apply(:lists, :nth, [1, [1,2,3]]) 
apply(module_name, method_name, [1, array]) 

如果您想了解更多关于灵药如何处理函数调用(和其他一切),你应该看一看quoteunquote

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

它返回函数调用的homoiconic表示形式。

{{:.,0,[:lists,:nth]},0,[1,[1,2,3]]} 

您可以Code.eval_quoted/3

{value, binding} = Code.eval_quoted(contents) 

编辑unquote引用的函数调用:这里是使用Enum.fetch用VAR一起的例子。

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

好。所以方法名称是一个原子。现在我认为这只是不允许我们在elixir中编写'module.method'的语法,对吧? – halfelf

+1

我相信你是对的。我认为做这个工作的唯一方法是在调用模块函数时改变使用原子的语法(即':lists。:nth')。我宁愿在这种情况下使用apply。 – lastcanal

+0

谢谢。这个答案是非常有帮助的。 – halfelf