为什么这两个 erlang 表达式序列的输出不同 shell?

Why the output differs in these two erlang expression sequence in shell?

在 Erlang 中 shell 为什么以下会产生不同的结果?

1> Total=15.    
2> Calculate=fun(Number)-> Total=2*Number end.
3> Calculate(6).   

异常错误:右侧值 12

不匹配
1> Calculate=fun(Number)-> Total=2*Number end.
2> Total=15.
3> Calculate(6). 

12

在第一种情况下,您将 Total 绑定为 15。在 Erlang 中,变量是 unmutable,但在 shell 中,当您编写 Total = 15. 时,您并没有真正创建变量 Total,shell 会尽力模仿如果您是 运行 应用程序时的行为,并且它存储在 table 对 {"Total",15}.

在下一行定义函数 Calculate。解析器找到表达式 Total=2*Number,并通过它的 table 来检测先前定义的 Total。评估变成了等同于 15 = 2*Number.

的东西

因此,在第三行中,当您要求计算 Calculate(6) 时,它会计算并计算 15 = 2*6 并发出错误消息

exception error: no match of right hand side value 12

在第二个例子中,当你定义函数时,Total还没有被定义。该函数存储时没有赋值(不再使用 Total),至少没有赋值给一个全局变量。所以定义 Total 时没有冲突,计算 Calculate(6).

时也没有错误

编译模块中的行为完全相同。

在 Erlang 中,= 运算符既是 赋值 又是 断言

如果我这样做:

A = 1,
A = 2,

我的程序会崩溃。我只是告诉它 A = 1 ,当 A 未绑定(还不作为标签存在)时,它现在永远被赋予值 1——直到执行范围发生变化。因此,当我告诉它 A = 2 时,它会尝试 断言 A 的值为 2,但事实并非如此。所以我们在一场糟糕的比赛中崩溃了。

Erlang 中的范围由两件事定义:

  • 当前函数的定义。在函数定义期间,此范围是 绝对
  • 当前 lambda 或列表理解的定义。此范围是 lambda 的 local,但也关闭了引用外部范围的任何值。

这些作用域总是在它们被声明时被外部作用域中的任何东西取代。这就是我们使用匿名函数创建闭包的方式。例如,假设我有一个要通过其发送数据列表的套接字。套接字已经绑定到函数头部的变量名 Socket,我们想使用列表操作映射值列表以发送到通过特定套接字发送的副作用。我可以关闭 lambda 主体内套接字的值,这具有从 "sending some data":

的更一般操作中柯里化该值的效果
send_stuff(Socket, ListOfMessages) ->
    Send = fun(Message) -> ok = gen_tcp:send(Socket, Message) end,
    lists:foreach(Send, ListOfMessages).

列表操作的每次迭代lists:foreach/2只能接受一个元数为1的函数作为它的第一个参数。我们已经创建了一个闭包,它已经在内部捕获了 Socket 的值(因为它已经绑定在外部作用域中)并将其与未绑定的内部变量 Message 组合在一起。另请注意,我们通过断言 gen_tcp:send/2 的 return 值是 really [=28= 来检查 gen_tcp:send/2 是否每次都在 lambda 中工作].

这是一个超级有用属性

考虑到这一点,让我们看看您的代码:

1> Total = 15.    
2> Calculate = fun(Number)-> Total = 2 * Number end.
3> Calculate(6).

在上面的代码中,您刚刚为 Total 分配了一个值,这意味着您已经为该值创建了一个标签(就像我们在上面的示例中分配了 Socket 一样)。然后你 断言 Total 的值是 2 * Number 可能的结果——这永远不可能是真的,因为 Total 是一个整数,所以 2 * 7.5 也不会削减它,因为结果将是 15.0,而不是 15.

1> Calculate = fun(Number)-> Total = 2 * Number end.
2> Total = 15.
3> Calculate(6).

不过,在此示例中,您有一个名为 Total 内部变量 ,它不会关闭在外部作用域中声明的任何值。后来,你在外部作用域中声明了一个名为Total的标签,但此时第一行的lambda定义已经转换为抽象函数和标签Total as used 那里已经完全交给了新函数定义的不可变 space 赋值给 Calculate 表示。因此,没有冲突。

考虑一下会发生什么,例如,尝试从列表理解中引用内部值:

1> A = 2.
2
2> [A * B || B <- lists:seq(1,3)].
[2,4,6]
3> A.
2
4> B.
* 1: variable 'B' is unbound

这不是您所期望的,比方说,Python 2:

>>> a = 2
>>> a
2
>>> [a * b for b in range(1,4)]
[2, 4, 6]
>>> b
3

顺便说一下,这已在 Python 3:

中修复
>>> a = 2                                                                                                                                                                                                                                                                    
>>> a                                                                                                                                                                                                                                                                        
2                                                                                                                                                                                                                                                                            
>>> [a * b for b in range(1,4)]
[2, 4, 6]                                                                                                                                                                                                                                                                    
>>> b                                                                                                                                                                                                                                                                        
Traceback (most recent call last):                                                                                                                                                                                                                                           
  File "<stdin>", line 1, in <module>                                                                                                                                                                                                                                        
NameError: name 'b' is not defined

(我也会提供一个 JavaScript 示例进行比较,但是那里的范围规则实在是太疯狂了,甚至都不重要...)

变量'Total'已经赋值为15,所以你不能在第二行使用相同的变量名Total。您应该更改为其他名称 Total1 或 Total2...