只是为了澄清命名,它们都是函数。一个是命名函数,另一个是匿名函数。但你是对的,它们的工作方式有些不同,我将说明它们为什么会这样工作。
让我们从第二个开始,fn
。fn
是一个闭包,类似于lambda
Ruby 中的 a。我们可以如下创建它:
x = 1
fun = fn y -> x + y end
fun.(2) #=> 3
一个函数也可以有多个子句:
x = 1
fun = fn
y when y < 0 -> x - y
y -> x + y
end
fun.(2) #=> 3
fun.(-2) #=> 3
现在,让我们尝试一些不同的东西。让我们尝试定义期望不同数量参数的不同子句:
fn
x, y -> x + y
x -> x
end
** (SyntaxError) cannot mix clauses with different arities in function definition
不好了!我们得到一个错误!我们不能混合使用不同数量参数的子句。函数始终具有固定的数量。
现在,让我们谈谈命名函数:
def hello(x, y) do
x + y
end
正如预期的那样,它们有一个名字,它们也可以接收一些参数。但是,它们不是闭包:
x = 1
def hello(y) do
x + y
end
此代码将无法编译,因为每次您看到 adef
时,都会得到一个空的变量范围。这是他们之间的一个重要区别。我特别喜欢这样一个事实,即每个命名函数都以全新的形式开始,并且您不会将不同范围的变量全部混合在一起。你有一个明确的界限。
我们可以将上面命名的 hello 函数作为匿名函数检索。你自己提到过:
other_function(&hello(&1))
然后你问,为什么我不能hello
像其他语言一样简单地传递它?这是因为 Elixir 中的函数是通过名称和数量来标识的。因此,期望两个参数的函数与期望三个参数的函数是不同的函数,即使它们具有相同的名称。因此,如果我们只是通过hello
,我们将不知道hello
您的实际意思。有两个、三个或四个参数的那个?这就是为什么我们不能使用具有不同元数的子句创建匿名函数的原因。
从 Elixir v0.10.1 开始,我们有一个语法来捕获命名函数:
&hello/1
这将捕获具有 arity 1 的本地命名函数 hello。在整个语言及其文档中,以这种hello/1
语法识别函数是很常见的。
这也是 Elixir 使用点来调用匿名函数的原因。由于您不能简单地hello
作为函数传递,而是需要显式捕获它,因此命名函数和匿名函数之间存在自然区别,并且调用每个函数的独特语法使所有内容更加明确(Lispers 会熟悉这一点由于 Lisp 1 与 Lisp 2 的讨论)。
总的来说,这就是为什么我们有两个函数以及为什么它们的行为不同的原因。