Erlang子列表功能表现

时间:2014-12-13 13:55:15

标签: erlang

我正在阅读http://learnyousomeerlang.com/,其中包含一个尾递归子列表函数,该函数反转列表以保持顺序。我写了一个不需要反向调用的替代方案。我的效率更高(当然更冗长,但我并不关心),还是我忽视了什么?

-module(sublist).

-export([sublist/2,sublistR/2]).

-include_lib("eunit/include/eunit.hrl").

sublist(_,0) ->
    [];
sublist([],_) ->
    [];
sublist(List,Length) ->
    sublist([hd(List)], tl(List), Length-1).

sublist(Acc,[],_) ->
    Acc;
sublist(Acc,_,0) ->
    Acc;
sublist(Acc,Tail,Length) ->
    sublist(Acc++[hd(Tail)], tl(Tail), Length-1).

sublistR(L, N) -> lists:reverse(sublistR(L, N, [])).

sublistR(_, 0, SubList) -> SubList;
sublistR([], _, SubList) -> SubList;
sublistR([H|T], N, SubList) when N > 0 ->
    sublistR(T, N-1, [H|SubList]).

sublist_test() ->
    sublisttest(fun sublist:sublist/2),
    sublisttest(fun sublist:sublistR/2).

sublisttest(SublistFunc) ->
    [] = SublistFunc([],10),
    [] = SublistFunc([1,2,3], 0),
    [1,2,3] = SublistFunc([1,2,3],3),
    [1,2,3] = SublistFunc([1,2,3],4),
    [1,2] = SublistFunc([1,2,3],2).

1 个答案:

答案 0 :(得分:4)

没有。但是不要感觉不好,这是每个人都必须尽早达成协议的事情。

关于这句话的全部内容:

Acc ++ [hd(Tail)]

我们说Acc = [1,2,3]是真的。然后Acc ++ [hd(Tail)]直接等同于[1,2,3 | [Head]]。这是什么意思?

在这种特殊情况下,它意味着完全与在Acc中作为汽车写出每个元素并使用{{1}的结果相同作为一个新的缺点的cdr。这意味着将单个元素连接到现有列表的末尾,必须遍历整个列表(用于解构,以显示最终的cdr)。另一方面,将单个元素添加到列表的开头是一个简单的操作。

使用[hd(Tail)]的神奇之处(以及使用cons风格列表的语言中的惯用语的全部原因)是它是用C语言实现的数组操作,而不是对象语言的扩展。然后重新写作。

这个访问最后一个元素的问题是昂贵的VS访问第一个元素是lists:reverse/1(或其他语言中的hd/1)返回列表的第一个元素的原因,但是{{1 (或其他语言中的head())返回除列表的第一个元素之外的所有内容。保证访问列表最后一个元素的函数的使用通常被“这很慢!”的警告所包围。

这也是为什么很少看到有人在几乎所有语言中使用tl/1的原因。如果确实需要从右侧折叠(例如,由于非交换操作),他们通常会先tail(),然后调用foldrlists:reverse/1可以是尾递归,但真正的foldl不能。

实际上,Erlang文档中提到了这一点:http://www.erlang.org/doc/man/lists.html#foldl-3

从右侧折叠的成本随着列表长度的增加而增加。这就是为什么会发生这种情况:

foldl

13μs与31291μs。哎哟。

另见: