【发布时间】:2023-03-05 17:58:02
【问题描述】:
我正在阅读 Dave 即将出版的关于 Elixir 的书,在一个练习中,我想根据字符串中一个字符的内容动态构造一个对 Kernel.+/2、Kernel.-/2 等的函数引用, '+'、'-' 等等。
基于另一个 SO 问题,我希望能够通过内核调用 apply/3,:+ 和两个这样的数字:
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
而且我猜没有办法使用apply/3 调用__op__。
当然,蛮力方法可以完成工作。
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)
【问题讨论】:
-
(与您的问题不太相关)我的解决方案看起来与您的完全不同! gist.github.com/nielsbom/0d689e00d927cc057ad7c5595d7e8f06
标签: function dynamic macros operator-keyword elixir