Erlang: List Comprehension, lists:map/2, lists:foreach/2 来使用

Erlang: List Comprehension, lists:map/2, lists:foreach/2 To use

List Comprehension 和lists 的效果:map/2,根据我的测试,后者的性能似乎更好。

参考 link 关于效率指南中的列表理解 用户指南:http://www.erlang.org/doc/efficiency_guide/listHandling.html#id66810

它说“如果显然不会使用列表理解的结果,则不会构造列表。

问题: 请问lists:map/2不使用(like lists:foreach/2)的结果和list一样吗理解 "a list will not be constructed" ??

检查这一点的一种方法是编写一个以明显无用的方式使用 lists:map/2 的函数,然后对其进行跟踪。考虑这个函数:

map() ->
    dbg:tracer(), dbg:p(self(), call),
    dbg:tpl(lists,map,c),
    lists:map(fun(X) -> X end, []),
    dbg:stop_clear().

如果您在模块 x 中实现此函数,编译它,然后在 shell 中生成对它的调用(生成消除了由 shell 本身引起的跟踪消息) ,您会看到如下内容:

3> spawn(x,map,[]).
<0.45.0>
(<0.45.0>) call lists:map(#Fun<dbg.2.121426812>,[]) ({lists,map,2})
(<0.45.0>) call lists:map(#Fun<x.0.133743400>,[]) ({x,map,0})
(<0.45.0>) call lists:map(#Fun<dbg.2.121426812>,[nonode@nohost]) ({dbg,
                                                                   do_ctp,2})
(<0.45.0>) call lists:map(#Fun<dbg.2.121426812>,[]) ({lists,map,2})
(<0.45.0>) call lists:map(#Fun<dbg.2.121426812>,[nonode@nohost]) ({dbg,
                                                                   do_ctp,2})

从trace输出的第三行可以看出,调用了匿名函数#Fun<x.0.133743400>,也就是我们x:map/0函数内部的匿名函数,调用者是{x,map,0},更为人所知的是 x:map/0。 (所有其他跟踪消息都是由我用于跟踪的 dbg 模块的调用引起的。)

另一种方法是使用 erlc 编译器的 -S 选项编译为汇编。首先消除 dbg 调用:

map() ->
    lists:map(fun(X) -> X end, []).

然后用-S编译,看结果:

{function, map, 0, 4}.
  {label,3}.
    {line,[{location,"x.erl",8}]}.
    {func_info,{atom,x},{atom,map},0}.
  {label,4}.
    {make_fun2,{f,15},0,0,0}.
    {move,nil,{x,1}}.
    {line,[{location,"x.erl",9}]}.
    {call_ext_only,2,{extfunc,lists,map,2}}.

这里的重要行是倒数第三行,{move,nil,{x,1}}.,它将一个空列表移动到 VM 寄存器中作为参数传递,最后一行是对 [=14 的调用=].

根据这些使用 Erlang 17.4 构建的结果,可以肯定地说您的问题的答案是否定的。

lists:map/2 是一个已经在你的系统中编译的函数,其实重点是 它是在你的代码 的单独模块中编译的,所以编译器不得对结果的使用与否做出任何假设。列表理解是在您的代码中编译的,因此可以执行优化

[edit] sorry,我没有重新检查,lists:map写成:

map(F, [H|T]) ->
    [F(H)|map(F, T)];
map(F, []) when is_function(F, 1) -> [].

不是列表理解!