2013-02-16 96 views
2

我还是新来的Erlang,并试图让我的脑袋无法改变变量。比方说,我创建一个堆栈,并希望添加一个新的元素。如果我无法将新值分配给我的列表,我将如何更新堆栈?我是否每次都必须创建一个新列表?在erlang中构建一个堆栈

比如我想推送可能看起来像

List = [X|List]. 

再流行是

[Head|Tail] = List 
Head 
List = Tail 

当然,这不会起作用,因为我无法改变的值的列表,这是我的问题。任何帮助表示赞赏。

回答

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. 

下面是如何一个示例使用它(我称为模块“和”):

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章。

2

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

预计每次分配一个新的变量,包含栈的修改版本,并使用从那里:

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), 
    ... 
...