Elixir:动态中缀运算符
我正试图编写一个简单的模块函数,它将一个表达式看作一个字符串,如"123 + 45"
并返回一个答案。所以:Elixir:动态中缀运算符
Calculator.calculate("123 + 45")
# => 168
我想到了刚刚在分裂的(" ")
字符串来获取整数和运营商的话,基本上调用它Code.eval_string
。这是我第一次尝试天真:
defmodule Calculator do
def calculate(str) do
[x, oper, y] = String.split(str, " ")
formula = "a = fn (c, d) -> c operator d end; a.(c, d)"
{answer, _ } = Code.eval_string(formula, [
c: String.to_integer(x),
operator: String.to_atom(oper),
d: String.to_integer(y)
])
answer
end
end
然后,在运行它,我收到此错误:
** (CompileError) nofile:1: undefined function c/1
(elixir) src/elixir_fn.erl:10: anonymous fn/3 in :elixir_fn.expand/3
(stdlib) lists.erl:1239: :lists.map/2
(elixir) src/elixir_fn.erl:14: :elixir_fn.expand/3
我无法弄清楚,为什么c
正在评估作为一个功能。我怀疑它与匿名函数中的operator
变量有关。我确认通过用硬编码算子重写它:
defmodule Calculator do
def calculate(str) do
[x, _, y] = String.split(str, " ")
formula = "a = fn (c, d) -> c + d end; a.(c, d)"
{answer, _ } = Code.eval_string(formula, [
c: String.to_integer(x),
d: String.to_integer(y)
])
answer
end
end
事实上,这产生了预期的结果。问题是:
为什么在匿名函数中存在operator
变量绑定导致c
被评估为函数?
它从文档出现在Code.eval_string
使它看起来好像变量绑定可以是任何东西,只要他们在关键字列表也就是第二个参数eval_string
会发现。
在我的第二次尝试中,我想过尝试将操作符从输入字符串转换为原子,并将运算符从中缀转换为函数调用(即从1 + 3
变为1.(:+, [3])
之类的东西。 。似乎是有效的语法
所以我的第二个问题是:
是否可以写一个表达中缀运算符,如+
的功能,然后才能够做动态定义操作员原子?
Why did the presence of the
operator
variable binding in the anonymous function causec
to be evaluated as a function?
代码c operator d
由Code.eval_string
解析为c(operator(d))
,就像它会在正常药剂,这意味着两个c
和operator
必须是元数1的功能该表达式是有道理的。你不会期望下面的代码可以工作,你会吗?
c = 1
operator = :+
d = 2
a = fn (c, d) -> c operator d end; a.(c, d)
Is it possible to write an expression with an infix operator such as
+
as a function, and then to be able do dynamically define that operator as an atom?
由于这些运营商都只是Kernel
模块定义的函数,你可以用apply/3
叫它:
iex(1)> c = 1
1
iex(2)> operator = :+
:+
iex(3)> d = 2
2
iex(4)> apply(Kernel, operator, [c, d])
3
所以在你的原代码,只需用apply(Kernel, operator, [c, d])
取代c operator d
。
这里也不需要eval_string
。
iex(1)> [a, b, c] = String.split("123 * 456", " ")
["123", "*", "456"]
iex(2)> a = String.to_integer(a)
123
iex(3)> b = String.to_atom(b)
:*
iex(4)> c = String.to_integer(c)
456
iex(5)> apply(Kernel, b, [a, c])
56088