【问题标题】:Erlang: List Comprehension, lists:map/2, lists:foreach/2 To useErlang: List Comprehension, lists:map/2, lists:foreach/2 使用
【发布时间】:2015-02-28 19:06:37
【问题描述】:

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

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

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

问题:我想知道 list:map/2 的结果是否不使用(如 lists:foreach/2),它是否与列表理解一样 " 列表将不被建造” ??

【问题讨论】:

  • 首先,您为什么要编写代码以使您无法判断是否使用了对lists:map/2 的调用结果?其次,我认为您对这种微优化的关注是错误的。
  • @SteveVinoski 这触及了我一直想解决的一个歧义点——以某种方式迭代列表之间的区别,专门针对这样做的副作用,而不是接收返回值。如果我们有一个 lists:map 或其他形式的列表理解,用于副作用而不是“价值构建,或者可能是副作用”,那么 ok | {error, {Index, Reason}} 可能是适当的返回类型,而不是未使用的列表,所以我们可以使用断言或错误类型代码,而不是让这种情况模棱两可。
  • 编译器或运行时如何知道这样的调用是否发生了副作用?
  • @SteveVinoski ,我知道如何使用它们,我刚刚阅读了效率指南用户指南并提出了这个问题。
  • lists:foreach/2 用于副作用,因为它从不构建值列表并返回 ok

标签: erlang


【解决方案1】:

检查这一点的一种方法是编写一个以明显无用的方式使用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&lt;x.0.133743400&gt;被调用了,也就是我们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 寄存器中作为参数传递,最后一行是对 lists:map/2 的调用。

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

【讨论】:

    【解决方案2】:

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

    [edit]对不起,我没有重新检查,lists:map是写的:

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

    不是列表理解!

    【讨论】:

    • 有趣的是,lists:map/2 使用了列表理解,我有这样的猜测,但是当我进行比较测试时,似乎列表理解成本更多时间???
    猜你喜欢
    • 2014-01-14
    • 1970-01-01
    • 2018-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 2016-08-22
    • 1970-01-01
    相关资源
    最近更新 更多