我正在学习Elixir,我目前正在处理捕获和部分应用功能的语义。
我已经熟悉fn(x)-> x + 1 end
语法。
我注意到了我认为令人惊讶的行为。例如,我观察到此代码段中的最后一个表达式返回false
:
f = &(&1 <> "-X")
# #Function<6.50752066/1 in :erl_eval.expr/5>
is_function f
# true
is_function &(&1 <> "-X")
# true
f == &(&1 <> "-X")
# false
我将其解释为意味着“动态地”重新创建匿名函数&(&1 <> "-X")
会返回一个不等于f
中存储的值的值(根据==
语义)。
那没关系,这个其他片段会证实我的理论:
g = &(String.upcase(&1))
# &String.upcase/1
is_function g
# true
is_function &(String.upcase(&1))
# true
g == &(String.upcase(&1))
# true
在那里,g
将等于重新创建的匿名函数,因为 - 可能 - 捕获现有的命名函数由编译器优化,并且每次返回相同的值。第二个片段中第一行的返回值似乎证实了这两种情况的处理方式不同。
然后我尝试使用“已知”功能,这意味着它已经存在:
f = &(&1 <> "-X")
z = &(f.(&1))
#Function<6.50752066/1 in :erl_eval.expr/5>
z == &(f.(&1))
# false
最后一句话再次是假的
我希望&(f.(&1))
的处理方式与&(String.upcase(&1))
类似,因为两者都已存在。
那么函数捕获的语义是什么?
答案 0 :(得分:5)
你是正确的,因为捕获一个命名函数是优化的,因此每次返回相同的值。
进一步下来我经常会通过&#34; funs&#34;来指代匿名函数。以及&#34; mfa&#34;。
的函数的模块,函数和arity捕获一个众所周知的匿名函数时没有相等性的事实与优化的工作方式有关。当从命名函数创建乐趣而不是存储整个匿名函数时,编译器存储模块名称,函数名称和命名函数的arity。使用&#34;重新捕获&#34;这是不可能的。一个众所周知的匿名函数 - 每次都必须创造一种新的乐趣。
免责声明:此分析不是基于对编译器的深入了解,而是基于对http://erlang.org/doc/apps/erts/erl_ext_dist.html中描述的外部术语格式(调用:erlang.term_to_binary/1
的结果)的熟悉。
ETF中的匿名函数有两种不同的类型:
这也让我们深入了解匿名函数的实现方式:每个模块都有一个表格,其中包含内部发生的所有匿名函数。我怀疑编译器正在进行某种类型的lambda提升以构造这样一个表。从命名函数创建的函数编码方式不同 - 作为mfa,可能不会存储在有趣的表中。