Elixir - 通过字符串名称调用模块上的方法

Elixir - Call method on module by String-name

总的来说,我对 Elixir 和函数式编程语言还很陌生。

在 Elixir 中,我想调用模块上的一个特定函数,给定模块名称为字符串。

我有以下(非常糟糕的)代码在工作,它几乎可以满足我的要求:

module_name = elem(elem(Code.eval_file("module.ex", __DIR__), 0), 1)
apply(module_name, :helloWorld, [])

这(至少据我所知)编译了当前目录中 module.ex 的(已编译)模块。我正在从两个元组和 运行 方法 helloWorld 中提取模块名称(不是字符串,不知道它实际上是什么数据类型)。

这段代码有两个问题:

  1. 它会打印出类似 redefining module Balance 的警告。我当然不希望这种情况发生在生产中。

  2. AFAIK 此代码编译 module.ex。但是由于 module.ex 已经编译和加载,它不希望这种情况发生。

我不需要通过文件名调用这些模块的方法,模块名也可以。但它确实必须是动态的,例如。在命令行输入 "Book" 应该在检查模块是否存在后调用函数 Book.helloWorld.

谢谢。

好吧,这就是提问的帮助所在:您一提问就会自己弄清楚。 ;)

现在正在使用 apply(String.to_existing_atom("Elixir.Module"), :helloWorld, [])。 (可能名字"Module"不被允许,不知道)

另请注意,模块名称是一个原子,因此通常不需要 String.to_existing_atom。考虑这段代码:

defmodule T do
  def first([]), do: nil
  def first([h|t]), do: h
end

在这种情况下,您可以简单地以这种方式进行应用:

apply(T,:first,[[1,2,3]])
#=> 1 

或者这个例子(下面的列表是 Elixir List 模块):

apply(List,:first,[[1,2,3]]) 
#=> 1

我的意思是,如果您知道模块的名称,则没有必要将其作为字符串传递,然后将字符串转换为现有原子。只需使用不带引号的名称。

请注意,您始终需要在模块前加上 "Elixir."

前缀
defmodule Test do
  def test(text) do
    IO.puts("#{text}")
  end
end

apply(String.to_existing_atom("Elixir.Test"), :test, ["test"])

打印"test" 和 returns {:ok}

这里做一个简单的解释:

假设您有这样的模块:

defmodule MyNamespace.Printer do
  def print_hello(name) do
    IO.puts("Hello, #{name}!")
  end
end

然后你有一个包含模块名称的字符串,你可以像这样在你的应用程序中传递它:

module_name = "Elixir.MyNamespace.Printer"

每当您收到字符串 module_name 时,您可以实例化一个模块并调用模块上的函数,如下所示:

module = String.to_existing_atom(module_name)
module.print_hello("John")

它将打印:

Hello, John!

另一种动态调用函数MyNamespace.Printer.print_hello/1的方法是:

print_hello_func = &module.print_hello/1
print_hello_func.("Jane")

它将打印:

Hello, Jane!

或者如果你想将 module_name 作为原子和 function_name 作为原子,并将它们传递到某个地方执行,你可以这样写:

a_module_name = :"Elixir.MyNamespace.Printer"
a_function_name = :print_hello

那么只要你有 a_module_name 和 a_function_name 作为原子,你就可以这样调用:

apply(a_module_name, a_function_name, ["Jackie"])

它将打印

Hello, Jackie!

当然你可以将 module_name 和 function_name 作为字符串传递,稍后将它们转换为原子。或者您可以将模块名称作为 atom 传递,并将函数作为对函数的引用传递。