私は 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)