【问题标题】:How to dynamically call an operator in Elixir如何在 Elixir 中动态调用运算符
【发布时间】:2023-03-05 17:58:02
【问题描述】:

我正在阅读 Dave 即将出版的关于 Elixir 的书,在一个练习中,我想根据字符串中一个字符的内容动态构造一个对 Kernel.+/2Kernel.-/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)

【问题讨论】:

标签: function dynamic macros operator-keyword elixir


【解决方案1】:

你可以只使用 Erlang 的:

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

我们知道这令人困惑,我们正在研究如何使其更明确或更透明。

更新:从 Elixir 1.0 开始,您可以直接分派到内核 (apply Kernel, :+, [1, 2]),甚至可以使用 OP 首次尝试的语法 (&Kernel.+/2)。

【讨论】:

  • 顺便说一句,既然 &Kernel.//2 不起作用,那么 / 宏的正确咒语是什么?
  • 应该这样做:&(Kernel./)/2(或&(&1 / &2)
  • 如果您将运算符作为单引号字符串,请使用 List.to_atom(operator) 作为应用的第二个参数
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-25
  • 1970-01-01
  • 2019-03-28
  • 1970-01-01
  • 2016-10-28
  • 1970-01-01
  • 2017-11-24
相关资源
最近更新 更多