Erlang没有共享内存.看一下sum函数,
sum(H|T)->H+sum(T);
sum([])->0
Run Code Online (Sandbox Code Playgroud)
因此sum([1,2,3])= 1 + 2 + 3 + 0
现在发生了什么?erlang是否创建了一个[1,1 + 2,1 + 2 + 3,1 + 2 + 3 + 0]的数组?
这是发生的事情:
sum([1,2,3]) = 1 + sum([2,3])
=> sum[2, 3] = 2 + sum([3])
=> sum([3]) = 3 + sum([])
=> sum([]) = 0
Run Code Online (Sandbox Code Playgroud)
现在sum([3])可以评估:
sum([3]) = 3 + sum([]) = 3 + 0 = 3
Run Code Online (Sandbox Code Playgroud)
这意味着sum([2, 3])可以评估:
sum([2, 3]) = 2 + sum([3]) = 2 + 3 = 5
Run Code Online (Sandbox Code Playgroud)
这意味着sum([1, 2, 3])可以评估:
sum([1,2,3]) = 1 + sum([2,3]) = 1 + 5 = 6
Run Code Online (Sandbox Code Playgroud)
回复评论:
好吧,我想你真正问的是什么immutable variables.假设您有以下C代码:
int x = 0;
x += 1;
Run Code Online (Sandbox Code Playgroud)
该代码是否以某种方式展示shared memory?如果没有,那么C不会对int变量使用共享内存......也不会使用erlang.
在C中你引入一个变量sum,给它一个初始值0,然后你给它添加值.Erlang没有这样做.Erlang做什么?
Erlang为每个递归函数调用在堆栈上分配一个新帧.每个帧存储局部变量及其值,例如参数变量,用于该特定函数调用.堆栈中可以存在多个帧,每个帧存储一个名为X的变量,但它们是单独的变量,因此没有任何X变量被突变 - 而是为每个新帧创建一个新的X变量,并给出新的X.一个新的价值.
现在,如果堆栈在erlang中真的像那样工作,那么执行数百万次的递归函数会向堆栈添加数百万个帧,并且在此过程中可能会耗尽其分配的内存并使程序崩溃.为了避免使用过多的内存,erlang使用了它tail call optimization,它允许函数使用的内存量保持不变.尾调用优化允许erlang用相同大小的后续帧替换堆栈中的第一帧,这使内存使用保持不变.此外,即使函数没有以尾递归格式定义,如sum()函数,erlang也可以优化代码,使其使用恒定的内存(参见Erlang性能的七个神话).
在您的sum()函数中,没有变量变异且没有共享内存.但实际上,函数参数变量确实像可变变量一样起作用.
我上面的第一个图表是堆栈的表示,为每个递归函数调用添加一个新帧.如果你重新定义sum()为尾递归,像这样:
sum(List)->
sum(List, 0).
sum([H|T], Total) ->
sum(T, Total+H);
sum([], Total)->
Total.
Run Code Online (Sandbox Code Playgroud)
接下来是一个递归函数执行图,表示在堆栈上被替换的帧以保持内存使用量不变:
sum([1, 2, 3]) => sum([1, 2, 3], 0) [H=1, T=[2,3], Total=0]
=> sum([2,3], 1) [H=2, T=[3], Total=1]
=> sum([3], 3]) [H=3, T=[], Total=3]
=> sum([], 6) [Total=6]
=> 6
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
131 次 |
| 最近记录: |