Можно ли определять Циркулярные Списки в Эрланге? - PullRequest
2 голосов
/ 16 января 2012

можно ли определить круговой список в эрланге?http://en.wikipedia.org/wiki/Linked_list

Первый вопрос: что именно означает круговой список в эрланге?это с двумя элементами, один элемент сам и рядом с ним адрес следующего элемента, хранящегося в списке?

, если так, я могу сказать, что существует возможность определения кругового списка в эрланге.но мне нужно уточнить погоду это то, что я думаю, что круговой список в эрланге?

Ответы [ 5 ]

8 голосов
/ 28 января 2012

Нет встроенного механизма списков для этого. Однако вы можете создать его, используя кортеж, содержащий элементы, которые вы посетили или нет.

Базовая структура - это кортеж с двумя списками: {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' и, возможно, 'delete' (см. Ниже), чтобы делать обычные вещи. Мы также могли бы добавить функцию «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};

И это должно охватывать большую часть этого.

5 голосов
/ 17 января 2012

Видя erlang и виртуальную машину erlang, поддерживает только неизменяемые данные, невозможно создать циклический список.Если вы построите его самостоятельно каким-то «нелегальным» способом, то нет уверенности, что управление памятью справится с этим должным образом.

4 голосов
/ 16 января 2012

В Erlang нет циклических списков, поддерживаемых виртуальной машиной. Вы должны построить их сами, если хотите.

3 голосов
/ 19 января 2012

почему да можно;)

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.
1 голос
/ 17 января 2012

Как указывалось выше, вам придется реализовать их самостоятельно.Но так как вы можете связывать данные с другими данными различными способами в Erlang, ничто не мешает вам сделать это.По сути, вам нужен только один элемент, представляющий текущий индекс, и другой, представляющий указатель на следующий индекс.Одним забавным способом было бы запустить процесс для каждого элемента в списке, указывающего на следующий (или предыдущий) процесс (элемент) по его PID.Один (или многие) процесс (ы) специального назначения может сканировать эти другие процессы "списка".Менее сумасшедшие подходы могут использовать ets или mnesia.

...