【问题标题】:How to create a very large list in erlang如何在erlang中创建一个非常大的列表
【发布时间】:2014-09-11 20:12:16
【问题描述】:

我们使用lists:seq(1,100)创建一个100个元素的列表,创建速度可以接受

但是如果我们创建一个包含 1000w 个元素​​的列表呢? lists:seq(1,10000000) 很慢。

您是否有任何想法创建一个没有任何数据副本的大型列表?

【问题讨论】:

    标签: erlang


    【解决方案1】:

    实际上,lists:seq/2 不执行任何复制。这是实际的实现:

    seq(First, Last)
        when is_integer(First), is_integer(Last), First-1 =< Last -> 
        seq_loop(Last-First+1, Last, []).
    
    seq_loop(N, X, L) when N >= 4 ->
        seq_loop(N-4, X-4, [X-3,X-2,X-1,X|L]);
    seq_loop(N, X, L) when N >= 2 ->
        seq_loop(N-2, X-2, [X-1,X|L]);
    seq_loop(1, X, L) ->
        [X|L];
    seq_loop(0, _, L) ->
        L.
    

    因此,它递归地调用自身,在每次调用中将最多 4 个元素添加到累加器(第三个参数)。将元素添加到列表中不涉及任何复制,并且调用是尾递归的,因此堆栈具有恒定大小。

    编辑:为了完整起见,我粘贴了 cmets 的一些说明: Erlang 列表是链表。在内部 [H|T],创建元素 H 并添加指向 T 的指针。T 永远不会被复制。你可以这样做,因为 T 是不可变的,它永远不会改变,即使你创建 [H1 | T], [H2 | T] 和 [H3 | T] - T 在它们之间共享。

    使用列表作为参数调用函数也不涉及复制:) 更大的数据结构(包括列表)存储在进程堆中。您只存储指向堆栈上第一个元素的指针。只有向另一个进程发送消息才会真正复制列表。

    创建非常大的列表可能会很慢,因为您可能会用完物理内存并开始使用交换。 编辑结束

    关于迭代,我想以 Nicolas Talfer 的回答为基础——你有时想将参数传递给循环调用的函数,你可以使用高阶函数和闭包来做到这一点:

    main(_) ->
        InitialArgs = 6,
        InitialFun = fun() -> do_stuff(InitialArgs) end,
        Ret = forloop(InitialFun, 5),
        io:format("~p", [Ret]).
    
    forloop(Fun, 1) ->
        Fun();
    forloop(Fun, Count) ->
        RetVal = Fun(),
        NewFun = fun() -> do_stuff(RetVal) end,
        forloop(NewFun, Count-1).
    
    do_stuff(Args) ->
        Args + 2.
    

    【讨论】:

    • 它仍然复制 [X-3,X-2,X-1,X|L] :(
    • 不,它没有。 Erlang 列表是链表。在内部 [H|T],创建元素 H 并添加指向 T 的指针。T 永远不会被复制。你可以这样做,因为 T 是不可变的,它永远不会改变,即使你创建 [H1 | T], [H2 | T] 和 [H3 | T] - T 在它们之间共享。
    • 是的,[H3 |T] 不会复制任何东西,但是当您将此列表作为参数传递给函数时,它会复制该列表:(
    • @CaTFooD 以列表为参数调用函数也不涉及复制 :) 更大的数据结构(包括列表)存储在进程堆中。您只存储指向堆栈上第一个元素的指针。只有向另一个进程发送消息才会真正复制列表。
    • @tkowal 这让我很困惑 :( 当我调用 lists:seq(1,20000000) 时。该过程需要大约 2G 内存来让 beam.smp 生成列表,但是当我存储生成的列表时list to ets,观察者说需要305M内存。
    【解决方案2】:

    我假设你想重复一个操作 N 次,独立于列表内容。 我错了吗? 即是这样的:

    [ fun() -> do_stuff() end || _X <- lists:seq(1,10000000) ].
    

    考虑改为这样做:

    foo(0) ->
        ok;
    foo(Count) ->
        do_stuff(),
        foo(Count-1).
    

    只需致电foo(10000000).

    【讨论】:

    • 这行得通,但我仍然想知道如何有效地创建如此大的列表
    【解决方案3】:

    事实上你可以尝试使用惰性列表: 它看起来像这样:

    seq(M, N) when M =< N ->
        fun() -> [M | seq(M+1, N)] end;
    seq(_, _) ->
        fun () -> [] end.
    
    E = seq(1, 1000000000000000).
    [1|NextElem] =  E().
    [2|NextElem2] = NextElem().
    

    【讨论】:

      猜你喜欢
      • 2018-08-20
      • 1970-01-01
      • 2015-08-18
      • 2011-07-21
      • 2013-07-03
      • 2010-09-13
      • 1970-01-01
      • 1970-01-01
      • 2013-05-27
      相关资源
      最近更新 更多