是否可以在erlang中定义循环列表? http://en.wikipedia.org/wiki/Linked_list
第一个问题是循环列表在erlang中的意思是什么? 它有两个元素,一个元素是它自己,旁边的元素是否存放在列表中?
如果是这样我可以说有可能在erlang中定义循环列表。 但我需要澄清天气是我认为循环列表是在erlang吗?
答案 0 :(得分:8)
没有内置列表机制来执行此操作。但是,您可以使用包含您访问过的元素的元组来构建一个元素。
基本结构是一个包含两个列表的元组:{Old, New}
。首次使用空列表时,它看起来像{[],[]}
。当您填写列表时,请将其填入New
列表:
new() -> {[], []}.
insert(X, {Old, New}) -> {Old, [X|New]}.
peek({_Old, [H|_]}) -> X.
要在列表中移动,您要做的是先在New
列表中搜索,然后将值放在旧列表中:
next({Old, [H|New]}) -> {[H|Old], New}.
这很好,就像我们只是丢弃旧元素一样。当我们到达列表末尾时会发生什么?我们需要修复这个函数(以及偷看函数):
peek({Old, []}) -> hd(lists:reverse(Old));
peek({_Old, [H|_]}) -> X.
next({Old, []}) ->
{[], lists:reverse(Old)}}.
next({Old, [H|New]}) ->
{[H|Old], New}}.
如果列表中没有任何内容,则会崩溃。如果你想通过特殊的套管它也可以返回'undefined':
next({[], []}) ->
undefined;
next({Old, []}) ->
{[], lists:reverse(Old)}.
next({Old, [H|New]}) ->
{[H|Old], New}.
然后,您可以使用“next”,“peek”和“删除”功能(见下文)来执行常规操作。我们还可以添加'prev'功能以允许向后浏览:
prev({[], []}) ->
undefined;
prev({[], New}) ->
{lists:reverse(New), Old}.
prev({[H|Old], New}) ->
{Old, [H|New]}.
delete({Old, []}) -> {[], tl(lists:reverse(Old))};
delete({Old,[H|New]}) -> {Old, New};
这应该涵盖大部分内容。
答案 1 :(得分:5)
看到erlang和erlang虚拟机只支持不可变数据,因此无法构建循环列表。如果你是以某种“非法”方式自己构建一个,那么不确定内存管理是否可以正确处理它。
答案 2 :(得分:4)
虚拟机支持的Erlang中没有循环列表。如果你需要,你必须自己构建它们。
答案 3 :(得分:3)
为什么你可以;)
14> X = ll:new().
20496
15> ll:push(X, 1).
1
16> ll:push(X, 2).
2
17> ll:push(X, 3).
3
18> ll:pop(X).
3
19> ll:hd(X).
2
20> {V0,R0} = ll:first(X).
{2,#Ref<0.0.0.80>}
21> {V1,R1} = ll:next(X, R0).
{1,#Ref<0.0.0.76>}
22> {V2,R2} = ll:next(X, R1).
{2,#Ref<0.0.0.80>}
这里有一些蹩脚的代码来证明它
-module(ll).
-export([new/0, delete/1, push/2, pop/1, first/1, hd/1, next/2]).
-define (META_KEY, '$meta_list').
-record(elt, {id, val, next}).
-record(meta, {id =?META_KEY, size, hd, tl}).
% Returns TID of ETS table representing linked list
new() ->
Tid = ets:new(alist,[{keypos, 2}]),
ets:insert(Tid, #meta{size=0, hd=undefined, tl=undefined}),
Tid.
% Delete list / ETS table representing linked list
delete(AList) ->
ets:delete(AList).
% Returns the value of what was pushed
push(AList, AnElt) ->
#meta{size = Size} = Meta = get_meta(AList),
Hd = get_head(AList, Meta),
Ref = make_ref(),
NewElt = #elt{id=Ref, val=AnElt, next=iif(Size, 0, Ref, Hd#elt.id)},
ets:insert(AList, NewElt),
case Size of
0 -> ets:insert(AList, Meta#meta{size=1,hd=Ref,tl=Ref});
N ->
Tl = get_tail(AList, Meta),
ets:insert(AList, Tl#elt{next = Ref}),
ets:insert(AList, Meta#meta{size=N+1,hd=Ref})
end,
AnElt.
% Returns the value of the popped element
pop(AList) ->
#meta{size = Size} = Meta = get_meta(AList),
Hd = get_head(AList, Meta),
case Size of
0 -> ok;
1 ->
ets:insert(AList, Meta#meta{size=0, hd=undefined,tl=undefined});
N ->
Next = get_next(AList, Hd),
Tail = get_tail(AList, Meta),
ets:insert(AList, Meta#meta{size=N-1, hd=Next#elt.id}),
ets:insert(AList, Tail#elt{next=Next#elt.id})
end,
ets:delete(AList, Hd#elt.id),
Hd#elt.val.
% Returns the value of the first element
hd(AList)->
{First, _Next} =first(AList),
First.
% Returns {val, ptr_to_tail}. The prt_to_tail can be used in next/2
first(AList)->
#meta{size = Size} = Meta = get_meta(AList),
if
Size == 0 -> {undefined, undefined};
true ->
Hd = get_head(AList, Meta),
{Hd#elt.val, Hd#elt.id}
end.
% Given ptr_to_tal, returns {hd(tail), ptr_to_tail}.
next(_AList, undefined) ->
{undefined, undefined};
next(AList, Id) ->
case ets:lookup(AList, Id) of
[] -> {error, node_missing};
[#elt{next=Next}] ->
case ets:lookup(AList, Next) of
[]-> {error, node_missing};
[#elt{val=Value}] ->
{Value, Next}
end
end.
%helper functions
get_meta(List)->
case ets:lookup(List, ?META_KEY) of
[] -> {error, corruptlist};
[Meta] -> Meta
end.
get_head(AList, #meta{size = Size, hd=Hd} ) ->
case Size of
0 -> #elt{};
_N ->
case ets:lookup(AList, Hd) of
[] -> {error, corruptlist};
[Head] -> Head
end
end.
get_tail(AList, #meta{size = Size, tl=Tl} ) ->
case Size of
0 -> #elt{};
_N ->
[Tail] = ets:lookup(AList, Tl),
Tail
end.
get_next(_AList, #elt{next=undefined}) -> #elt{};
get_next(AList, #elt{next=Next}) ->
case ets:lookup(AList, Next) of
[] -> {error, corruptlist};
[Elt] -> Elt
end.
iif(A, B, TruePart, ElsePart)->
case A == B of
true -> TruePart;
false -> ElsePart
end.
答案 4 :(得分:1)
如上所述,您必须自己实施它们。但是,由于您可以在erlang中以各种方式将数据与其他数据相关联,因此没有什么可以阻止您这样做。 基本上,您只需要一个表示当前索引的东西,另一个表示指向下一个索引的指针。一种有趣的方式是为列表中的每个元素启动一个过程,该过程通过其PID指向下一个(或前一个)过程(元素)。一个(或许多)特殊目的进程可以抓取其他“列表”进程。不那么疯狂的aproaches可能会使用ets或mnesia。