5

私は Dave の Elixir に関する近刊の本に取り組んでいます。ある演習では、文字列の 1 文字の内容に基づいて、 などへの関数参照を動的に作成したいと考えていKernel.+/2ます。Kernel.-/2'+''-'

apply/3別のSOの質問に基づいて、渡すカーネル、:+、および次のような2つの数字を呼び出すことができると予想しました:

apply(Kernel, :+, [5, 7])

(私が正しく理解していれば)Kernel.+/2関数ではなくマクロであるため、これは機能しません。ソース コードを調べたところ、+で定義されて__op__おり、 から呼び出すことができますiex

__op__(:+, 5, 7)

これは、:+ を変数に入れるまで機能します。

iex(17)> h = list_to_atom('+')
:+
iex(18)> __op__(h, 5, 7)
** (CompileError) iex:18: undefined function __op__/3
    src/elixir.erl:151: :elixir.quoted_to_erl/3
    src/elixir.erl:134: :elixir.eval_forms/4

__op__そして、を使用して呼び出す方法はないと思いapply/3ます。

もちろん、力ずくの方法で仕事を完了できます。

  defp _fn(?+), do: &Kernel.+/2
  defp _fn(?-), do: &Kernel.-/2
  defp _fn(?*), do: &Kernel.*/2
# defp _fn(?/), do: &Kernel.//2   # Nope, guess again
  defp _fn(?/), do: &div/2        # or &(&1 / &2) or ("#{div &1, &2} remainder #{rem &1, &2}")

しかし、もっと簡潔でダイナミックなものはありますか?


José Valim は、以下の回答でそれを釘付けにしました。コンテキスト内のコードは次のとおりです。

  def calculate(str) do 
    {x, op, y} = _parse(str, {0, :op, 0})
    apply :erlang, list_to_atom(op), [x, y]
  end

  defp _parse([]     , acc      )                 , do: acc
  defp _parse([h | t], {a, b, c}) when h in ?0..?9, do: _parse(t, {a, b, c * 10 + h - ?0})
  defp _parse([h | t], {_, _, c}) when h in '+-*/', do: _parse(t, {c, [h], 0})
  defp _parse([_ | t], acc      )                 , do: _parse(t, acc)
4

1 に答える 1

12

Erlang のものをそのまま使用できます。

apply :erlang, :+, [1,2]

これが紛らわしいことは承知しており、それをより明確に、またはより透明にする方法を研究しています。

更新: Elixir 1.0 以降、カーネルに直接ディスパッチすることも ( apply Kernel, :+, [1, 2])、OP が最初に試みた構文を使用することもできます ( &Kernel.+/2)。

于 2014-01-12T20:55:47.670 に答える