web-dev-qa-db-ja.com

Elixir-文字列名でモジュールのメソッドを呼び出す

私はElixirと関数型プログラミング言語全般にかなり慣れていません。

Elixirでは、モジュール名をStringとして指定して、モジュールで1つの特定の関数を呼び出したいと思います。

私は次の(非常に悪い)コードを動作させています。これは私がやりたいことをほぼ実行します。

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

これは(少なくとも私が理解しているように)現在のディレクトリにあるmodule.exの(コンパイル済みの)モジュールをコンパイルします。 2つのタプルからモジュール名(文字列としてではなく、実際のデータ型がわからない)を抽出し、その上でメソッドhelloWorldを実行します。

このコードには2つの問題があります。

  1. redefining module Balanceのような警告を出力します。私は確かにそれが本番環境で発生することを望んでいません。

  2. このコードはmodule.exをコンパイルします。しかし、module.exはすでにコンパイルおよびロードされているため、それが発生することは望ましくありません。

これらのモジュールのメソッドをファイル名で呼び出す必要はありません。モジュール名も問題ありません。しかし、それは動的である必要があります。コマンドラインで「Book」と入力すると、モジュールが存在するかどうかを確認した後、関数Book.helloWorldを呼び出す必要があります。

ありがとう。

13
lschuermann

さて、それは尋ねることが役立つところです:あなたはあなたが尋ねた瞬間にそれを自分で理解するでしょう。 ;)

今はapply(String.to_existing_atom("Elixir.Module"), :helloWorld, [])を使用するだけです。 (たぶん「モジュール」という名前は許可されていません、わかりません)

15
lschuermann

モジュールには常に「Elixir」というプレフィックスを付ける必要があることに注意してください。

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

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

「test」を出力し、{:ok}を返します

8
JasonG

また、モジュールの名前はatomなので、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

つまり、モジュールの名前がわかっている場合は、それを文字列として渡してから、その文字列を既存のアトムに変換する必要はありません。名前は引用符なしで使用してください。

4

ここに簡単な説明があります:

次のようなモジュールがあると仮定します。

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をatomとして、function_nameをatomとして、どこかに実行するためにそれらを渡す場合は、次のように記述できます。

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として渡し、関数を関数への参照として渡すことができます。

1