Erlang 没有共享内存.那么,例如,sum函数会发生什么?



Erlang 没有共享内存。看看求和函数,

sum(H|T)->H+sum(T);
sum([])->0

所以 总和([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

现在可以评估sum([3])

sum([3]) = 3 + sum([]) = 3 + 0 = 3

这意味着可以评估sum([2, 3])

sum([2, 3]) = 2 + sum([3]) = 2 + 3 = 5

这意味着可以评估sum([1, 2, 3])

sum([1,2,3]) = 1 + sum([2,3]) = 1 + 5 = 6

对评论的回应:

好吧,我想你真正问的是immutable variables. 假设您有以下 C 代码:

int x = 0;
x += 1;

该代码是否以某种方式演示了shared memory? 如果不是,那么 C 不会对 int 变量使用共享内存......二郎也没有。

在 C 中,你引入一个变量,求和,给它一个初始值,0,和 之后,您向其添加值。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.

下面是一个递归函数执行的图,它表示在堆栈上替换帧以保持内存使用量不变:

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

您正在进行递归调用。 每个函数体的作用域在返回某些内容之前不会终止。 因此,每次调用的不变变量 H 将一直保留到基本情况发生。

在函数参数中累加器的帮助下,它可以是尾递归的,通过先计算 H 部分,然后调用后继递归并将计算值作为累加器,在内存上更轻。

因此,在这两种方式中,都不会在函数作用域之外使用任何内容。

相关内容

最新更新