我对Erlang还是个新手,正在努力解决无法更改变量的问题。假设我创建了一个堆栈,并想添加一个新元素。如果我不能为列表赋值,我将如何更新堆栈?我是否每次都必须创建一个新列表?例如,我想Push可以看起来像这样
List = [X|List].
然后流行音乐就是
[Head|Tail] = List Head List = Tail
当然,这是行不通的,因为我不能改变列表的值,这是我的问题。任何帮助都是感激不尽的。
vxqlmq5t1#
Erlang不能在函数中有副作用,这是functional programming中常见的一个特性。改变变量状态是一个副作用。Erlang中所有的状态改变都被进程和消息传递隐藏起来,在actor model中。“更改”变量的常见方法是函数使用更改后的变量调用自身,该变量称为recursion。例如,要对列表中的所有元素求和:
sum([]) -> 0; sum([H|Tail]) -> H + sum(Tail).
更好的方法是让你的函数尾部递归,这意味着它们把自己作为函数体的最后一条指令来调用。这将保存内存,因为不是所有的函数调用都需要保留在堆栈(tail-call optimization)上。同样的例子,但使用尾部递归:
sum([], Acc) -> Acc; sum([H|Tail], Acc) -> sum(Tail, Acc + H). sum(L) -> sum(L, 0).
在这个例子中,我引入了一个累加器变量来沿着中间结果。如何消除程序的副作用并不总是显而易见的,特别是当你试图从程序的Angular 考虑问题时(如在C或Java中)。这需要实践,可能还需要在更理论的层面上理解函数式编程的意愿。纯函数式编程语言根本不会有任何副作用;函数的返回值必须只基于函数的输入参数,并且函数的唯一输出必须是返回值。Erlang * 不是 * 这种情况。recieve子句和!运算符用于函数内部的输入和输出;外部状态可以保存为进程,您可以向其发送消息并获得回复。下面是一个如何创建一个变量的例子,该变量的值可以通过消息传递来更改(试着弄清楚var_proc是否是尾递归的!):
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章节中找到。
w9apscun2#
对于函数式编程语言(如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), ... ...
2条答案
按热度按时间vxqlmq5t1#
Erlang不能在函数中有副作用,这是functional programming中常见的一个特性。改变变量状态是一个副作用。Erlang中所有的状态改变都被进程和消息传递隐藏起来,在actor model中。
“更改”变量的常见方法是函数使用更改后的变量调用自身,该变量称为recursion。例如,要对列表中的所有元素求和:
更好的方法是让你的函数尾部递归,这意味着它们把自己作为函数体的最后一条指令来调用。这将保存内存,因为不是所有的函数调用都需要保留在堆栈(tail-call optimization)上。同样的例子,但使用尾部递归:
在这个例子中,我引入了一个累加器变量来沿着中间结果。
如何消除程序的副作用并不总是显而易见的,特别是当你试图从程序的Angular 考虑问题时(如在C或Java中)。这需要实践,可能还需要在更理论的层面上理解函数式编程的意愿。
纯函数式编程语言根本不会有任何副作用;函数的返回值必须只基于函数的输入参数,并且函数的唯一输出必须是返回值。Erlang * 不是 * 这种情况。
recieve
子句和!
运算符用于函数内部的输入和输出;外部状态可以保存为进程,您可以向其发送消息并获得回复。下面是一个如何创建一个变量的例子,该变量的值可以通过消息传递来更改(试着弄清楚
var_proc
是否是尾递归的!):下面是一个如何使用它的例子(我把这个模块称为“sum”):
更多的例子和一些动机可以在Erlang文档的Concurrent Programming章节中找到。
w9apscun2#
对于函数式编程语言(如Erlang)中的堆栈数据结构,通常不会创建单独的进程来包含堆栈数据结构并使用消息对其进行操作,除非希望多个进程同时访问堆栈数据结构。
你被期望每次都分配一个新变量,以包含修改后的堆栈版本,并从那里开始使用:
您可以这样使用它: