我還是新來的Erlang,並試圖讓我的腦袋無法改變變量。比方說,我創建一個堆棧,並希望添加一個新的元素。如果我無法將新值分配給我的列表,我將如何更新堆棧?我是否每次都必須創建一個新列表?在erlang中構建一個堆棧
比如我想推送可能看起來像
List = [X|List].
再流行是
[Head|Tail] = List
Head
List = Tail
當然,這不會起作用,因爲我無法改變的值的列表,這是我的問題。任何幫助表示讚賞。
我還是新來的Erlang,並試圖讓我的腦袋無法改變變量。比方說,我創建一個堆棧,並希望添加一個新的元素。如果我無法將新值分配給我的列表,我將如何更新堆棧?我是否每次都必須創建一個新列表?在erlang中構建一個堆棧
比如我想推送可能看起來像
List = [X|List].
再流行是
[Head|Tail] = List
Head
List = Tail
當然,這不會起作用,因爲我無法改變的值的列表,這是我的問題。任何幫助表示讚賞。
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章。
對於像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),
...
...