在erlang中构建堆栈

时间:2013-02-16 15:45:24

标签: erlang stack

我还是Erlang的新手并试图让我的头脑无法改变变量。让我们说我创建一个堆栈,并希望添加一个新元素。如果我无法为列表分配新值,我将如何更新堆栈?我每次都必须创建一个新列表吗?

例如,我认为Push看起来像

List = [X|List].

然后一个Pop将是

[Head|Tail] = List
Head
List = Tail

当然这不起作用,因为我无法更改List的值,而且存在我的问题。任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:3)

对于像Erlang这样的函数式编程语言中的堆栈数据结构,通常不会遇到创建单独的进程以包含消息并对其执行操作的麻烦,除非您希望多个进程同时访问它。 / p>

期望每次都分配一个新变量,以包含堆栈的修改版本并从中使用该变量:

new() -> [].

push(Value, OldStack) -> [Value|OldStack].

pop([]) -> empty;
pop([Value|RestStack]) -> {Value, RestStack}.

你这样使用它:

S0 = new(),
S1 = push(a, S0),
S2 = push(b, S1),
S3 = push(c, S2),
...
case pop(SN) of
   empty -> stack_was_empty;
   {Element, SN1} ->
     do_something_with(Element),
     SN2 = push(b, SN1),
     ...
...

答案 1 :(得分:2)

Erlang不能在函数内部产生副作用,这是functional programming种语言中常见的一种功能。改变变量状态是副作用。 Erlang中的所有状态更改都被进程和消息传递隐藏,在所谓的actor model中。

“更改”变量的常用方法是让函数使用已更改的变量调用自身,该变量称为recursion。例如,要汇总列表的所有元素:

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

更好的是使你的函数tail recursive,这意味着它们将自己称为函数体中的最后一条指令。这将节省内存,因为并非所有函数调用都需要保留在堆栈上(tail-call optimization)。相同的例子,但使用尾递归:

sum([], Acc) -> Acc;
sum([H|Tail], Acc) -> sum(Tail, Acc + H).

sum(L) -> sum(L, 0).

在这个例子中,我引入了一个累加器变量来传递中间结果。

如何使程序副作用免费并不总是显而易见的,特别是如果你试图用程序术语来思考问题(如在C或Java中)。它需要实践,并且可能需要在更理论的层面上理解函数式编程。

纯函数式编程语言根本不会产生任何副作用;函数的返回值必须仅基于函数的输入参数,并且函数的唯一输出必须是返回值。这是 Erlang的情况。 recieve子句和!运算符用于函数内的输入和输出;副作用。外部状态可以作为您可以发送消息并获得回复的进程保存。

以下是如何创建一个可以通过消息传递更改其值的变量的示例(尝试确定var_proc是否为尾递归!):

var_proc(Value) ->
    receive
    {get, Pid} ->
            Pid ! {value, Value},
            var_proc(Value);
        {set, New} -> 
            var_proc(New)
    end.

var_start(Init) ->
    spawn(?MODULE, var_proc, [Init]).

var_set(Pid, Value) ->
    Pid ! {set, Value}.

var_get(Pid) ->
    Pid ! {get, self()},
    receive
    {value, Value} -> Value
    end.

以下是如何使用它的示例(我将模块称为“sum”):

13> V = sum:var_start(6).
<0.72.0>
14> sum:var_get(V).
6
15> sum:var_set(V, 10).
{set,10}
16> sum:var_get(V).    
10

更多示例和一些动机可以在Erlang文档的Concurrent Programming章节中找到。