6

给定 Erlang 中的任何列表,例如:

L = [foo, bar, foo, buzz, foo].

如何使用递归函数仅显示该列表的唯一项目?我不想使用内置函数,例如列表函数之一(如果存在)。

在我的示例中,我想去的地方将是一个新列表,例如

SL = [bar, buzz].

我的猜测是,在应用过滤器之前,我会先使用快速排序功能对列表进行排序?

任何的意见都将会有帮助。该示例是 Cesarini 和 Thompson 的优秀“Erlang Programming”一书第 3 章中练习的变体。

4

9 回答 9

7

我建议这个:

unique(L) ->
    unique([],L).
unique(R,[]) -> R; 
unique(R,[H|T]) ->
    case member_remove(H,T,[],true) of
        {false,Nt} -> unique(R,Nt);
        {true,Nt} -> unique([H|R],Nt)
    end.

member_remove(_,[],Res,Bool) -> {Bool,Res};
member_remove(H,[H|T],Res,_) -> member_remove(H,T,Res,false);
member_remove(H,[V|T],Res,Bool) -> member_remove(H,T,[V|Res],Bool).

member_remove 函数一次性返回剩余的尾部,而不会检查所有出现的元素是否重复和测试结果。

于 2013-03-13T20:56:34.247 回答
3

我可以这样做:)

get_unique(L) ->
    SortedL = lists:sort(L),
    get_unique(SortedL, []).

get_unique([H | T], [H | Acc]) ->
    get_unique(T, [{dup, H} | Acc]);
get_unique([H | T], [{dup, H} | Acc]) ->
    get_unique(T, [{dup, H} | Acc]);
get_unique([H | T], [{dup, _} | Acc]) ->
    get_unique(T, [H | Acc]);
get_unique([H | T], Acc) ->
    get_unique(T, [H | Acc]);
get_unique([], [{dup, _} | Acc]) ->
    Acc;
get_unique([], Acc) ->
    Acc.
于 2013-03-14T09:40:32.257 回答
1

我认为想法可能是:检查您是否已经看到列表的头部。如果是这样,跳过它并递归检查尾部。如果不是 - 将当前头部添加到结果中,以“看到”并递归检查尾部。检查您是否已经看到该项目的最合适的结构已设置。

所以,我建议如下:

 remove_duplicates(L) -> remove_duplicates(L,[], sets:new()). 

  remove_duplicates([],Result,_) -> Result;
  remove_duplicates([Head|Tail],Result, Seen) ->
    case sets:is_element(Head,Seen) of
      true -> remove_duplicates(Tail,Result,Seen);
      false -> remove_duplicates(Tail,[Head|Result], sets:add_element(Head,Seen))
    end.
于 2013-03-14T18:24:25.307 回答
1

使用两个蓄能器。一个保留您目前看到的元素,一个保留实际结果。如果您是第一次看到该项目(不在“已见列表”中),请将该项目添加到两个列表并递归。如果您以前看过该项目,请在递归之前将其从结果列表 (Acc) 中删除。

-module(test).

-export([uniques/1]).

uniques(L) ->
    uniques(L, [], []).

uniques([], _, Acc) ->
    lists:reverse(Acc);
uniques([X | Rest], Seen, Acc) ->
    case lists:member(X, Seen) of
        true -> uniques(Rest, Seen, lists:delete(X, Acc));
        false -> uniques(Rest, [X | Seen], [X | Acc])
    end.
于 2013-03-13T19:24:12.547 回答
1
unique(List) ->
    Set = sets:from_list(List),
    sets:to_list(Set).
于 2017-10-03T10:49:08.850 回答
0

试试下面的代码

-module(util).

-export([unique_list/1]).

unique_list([]) -> [];
unique_list(L)  -> unique_list(L, []).

% Base Case
unique_list([], Acc) -> 
    lists:reverse(Acc);

% Recursive Part 
unique_list([H|T], Acc) ->
    case lists:any(fun(X) -> X == H end, T) of
        true  -> 
            unique_list(lists:delete(H,T), Acc);
        false -> 
            unique_list(T, [H|Acc])
end.
于 2013-03-15T00:31:32.077 回答
0

此解决方案仅过滤掉列表中的重复项。可能需要建立在它做你想做的事。

删除重复项(列表)->
    列表:反向(删除(列表,[]))。

删除([],这个)->这个;
移除([A|Tail],Acc)->
    删除(删除全部(A,尾巴),[A|Acc])。

delete_all(Item, [Item | Rest_of_list]) ->
    delete_all(Item, Rest_of_list);
delete_all(Item, [Another_item| Rest_of_list]) ->
    [另一个项目 | delete_all(Item, Rest_of_list)];
删除全部(_,[])-> []。

编辑


Microsoft Windows [版本 6.1.7601]
版权所有 (c) 2009 Microsoft Corporation。版权所有。

C:\Windows\System32>erl
Eshell V5.9(使用 ^G 中止)
1> 列表 = [1,2,3,4,a,b,e,r,a,b,v,3,2,1,g,{红,绿},d,2,5,6,1 ,4,6,5,{红,绿}]。
[1,2,3,4,a,b,e,r,a,b,v,3,2,1,g,
 {红,绿},
 d,2,5,6,1,4,6,5,
 {红,绿}]
2> 删除重复项(列表)。
[1,2,3,4,a,b,e,r,v,g,{红,绿},d,5,6]
3>

于 2013-03-13T20:13:52.427 回答
0

唯一(L)-> 集:to_list(集:from_list(L))。

于 2017-03-02T14:30:13.807 回答
-1

最简单的方法是使用带有“累加器”的函数来跟踪您已经拥有的元素。所以你会写一个像

% unique_acc(累加器,List_to_take_from)。

通过不导出累加器版本,而是导出其调用者,您仍然可以拥有一个干净的功能:

-module(uniqueness).
-export([unique/1]).

unique(List) ->
    unique_acc([], List).

如果要从中获取的列表为空,则您已完成:

unique_acc(Accumulator, []) ->
    Accumulator;

如果不是:

unique_acc(Accumulator, [X|Xs]) ->
   case lists:member(X, Accumulator) of
       true  -> unique_acc(Accumulator, Xs);
       false -> unique_acc([X|Accumulator], Xs)
   end.

需要注意的两件事:
-- 这确实使用了一个列表 BIF -- lists:member/2。不过,您可以轻松地自己编写。
-- 元素的顺序是颠倒的,从原始列表到结果。如果你不喜欢这样,你可以定义unique/1lists:reverse(unique_acc([], List)). 或者更好的是,自己编写一个反向函数!(这很简单)。

于 2013-03-13T18:55:15.210 回答