Gre*_*reg 5 erlang erlang-shell
在Erlang shell中为什么以下产生不同的结果?
1> Total=15.
2> Calculate=fun(Number)-> Total=2*Number end.
3> Calculate(6).
Run Code Online (Sandbox Code Playgroud)
异常错误:右侧值12不匹配
1> Calculate=fun(Number)-> Total=2*Number end.
2> Total=15.
3> Calculate(6).
Run Code Online (Sandbox Code Playgroud)
12
在Erlang中,=运算符既是赋值又是断言.
如果我这样做:
A = 1,
A = 2,
Run Code Online (Sandbox Code Playgroud)
我的程序会崩溃.我刚刚告诉它A = 1,当A它未绑定时(尚未作为标签存在),它现在永远被赋予值1 - 直到执行范围发生变化.那么当我告诉它A = 2它试图断言值为A2时,它不是.所以我们在糟糕的比赛中遇到了崩溃.
Erlang中的范围由两件事来定义:
这些范围在它们被外部范围内的任何内容声明时总是被取代.这就是我们用匿名函数创建闭包的方法.例如,假设我有一个套接字我想通过发送数据列表.套接字已经绑定到函数头部的变量名,我们希望使用列表操作映射要发送到该特定套接字的副作用的值列表.我可以在lambda体内关闭套接字的值,这样可以将"发送一些数据"的更一般操作中的值调整为:Socket
send_stuff(Socket, ListOfMessages) ->
Send = fun(Message) -> ok = gen_tcp:send(Socket, Message) end,
lists:foreach(Send, ListOfMessages).
Run Code Online (Sandbox Code Playgroud)
列表操作的每次迭代lists:foreach/2只能接受arity 1的函数作为其第一个参数.我们已经创建了一个闭包,它Socket已经捕获了内部的值(因为它已经绑定在外部作用域中)并将它与未绑定的内部变量结合起来Message.还需要注意的是,我们正在检查是否gen_tcp:send/2由声称的返回值每次工作的拉姆达内gen_tcp:send/2是真的 ok.
这是一个非常有用的属性.
所以考虑到这一点,让我们来看看你的代码:
1> Total = 15.
2> Calculate = fun(Number)-> Total = 2 * Number end.
3> Calculate(6).
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,您刚刚为其分配了一个值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).
Run Code Online (Sandbox Code Playgroud)
在这个例子中,虽然你有一个内部的变量称为Total不关闭了在外部范围声明的任何值.后来,你的声明中称外部范围的标签Total,但这次在第一行的拉姆达的定义已被转换为一个抽象的函数和标签Total作为那里使用已经完全移交给新功能的不可变空间定义要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
Run Code Online (Sandbox Code Playgroud)
这不是你所期望的,比方说,Python 2:
>>> a = 2
>>> a
2
>>> [a * b for b in range(1,4)]
[2, 4, 6]
>>> b
3
Run Code Online (Sandbox Code Playgroud)
顺便说一句,这已在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
Run Code Online (Sandbox Code Playgroud)
(我会提供一个JavaScript示例进行比较,但是那里的范围规则是如此绝对疯狂,甚至不重要......)
| 归档时间: |
|
| 查看次数: |
83 次 |
| 最近记录: |