只是为了澄清命名,它们都是函数。一个是命名函数,另一个是匿名函数。但你是对的,它们的工作方式有些不同,我将说明它们为什么会这样工作。
让我们从第二个 fn 开始。 fn 是一个闭包,类似于 Ruby 中的 lambda。我们可以这样创建它:
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
此代码将无法编译,因为每次看到def,都会得到一个空的变量范围。这是他们之间的一个重要区别。我特别喜欢这样一个事实,即每个命名函数都以全新的方式开始,并且您不会将不同范围的变量全部混合在一起。你有一个明确的界限。
我们可以将上面命名的 hello 函数作为匿名函数检索。你自己提到过:
other_function(&hello(&1))
然后你问,为什么我不能像其他语言那样简单地将它传递为hello?这是因为 Elixir 中的函数由名称 和 arity 标识。因此,期望两个参数的函数与期望三个参数的函数是不同的函数,即使它们具有相同的名称。因此,如果我们只是通过hello,我们将不知道您的实际意思是哪个hello。有两个、三个或四个参数的那个?这就是为什么我们不能创建带有不同参数的子句的匿名函数的原因。
从 Elixir v0.10.1 开始,我们有了一个语法来捕获命名函数:
&hello/1
这将捕获具有 arity 1 的本地命名函数 hello。在整个语言及其文档中,以 hello/1 语法识别函数是很常见的。
这也是 Elixir 使用点来调用匿名函数的原因。由于您不能简单地将hello 作为函数传递,而是需要显式捕获它,命名函数和匿名函数之间存在自然区别,并且调用每个函数的独特语法使所有内容都更加明确(Lispers 将是由于 Lisp 1 与 Lisp 2 的讨论而熟悉这一点)。
总的来说,这就是我们有两个函数以及它们行为不同的原因。