轻松破解foldl的方法

8

我需要从foldl中退出。这里是一个虚拟的例子,演示如何在计算列表中值的总和并遇到太大的值(即10)时从fold中退出。

   L = [1,2,3,4,10,5,6,7],

   Res = 
      try
         lists:foldl(
            fun(I, Value) ->
               if (I < 10) ->
                  Value + I;
               true ->
                  throw({too_big_value, Value})
               end
            end,
            0, L)
      catch
         throw:{too_big_value, Value} -> Value
      end,

   Res.

我知道这个例子是人为的,但是有没有什么好方法可以打破fold(我知道fold总是扫描整个结构)?
请注意,即使我从fold中退出,我也需要检索正确的数据。在这种情况下,我应该从上一次迭代中获取数据(就像我的示例中所做的那样)。

“即使我从折叠中退出,我仍需要检索正确的数据”,这是什么意思?您需要包含抛出所做更改的列表吗? - Isac
2个回答

8

我很好奇,在这里使用foldl的目的是什么?如果需要跳出,请使用递归,因为foldl不是为此设计的。

main([]) ->
  L = [1,2,3,4,5,10,6,7],

   io:format("[~w]", [s(L, 0)]).

s([], S) ->
  S;

s([H|T], S) ->
  if (H < 10) ->
    s(T, S + H);
  true ->
    S
  end.

更新:

另一个选项是使用takewhile

lists:foldl(fun(E, A) -> A + E end, 0, lists:takewhile(fun(E) -> E < 10 end, L))

谢谢您的回复。我正在尝试找到更易读的代码方式。我是Erlang的新手,正在努力理解最好的做法。我认为您的第二个变量比我的和您的第一个变量更好。 - ravnur
@usr 只有第二个变量可能会创建额外的列表,从而消耗内存(除非编译器可以进行优化),而第一个则不会。 - Victor Moroz

7

你做得很好,使用try/catch的throw进行非本地返回。如果函数查看fun的返回值来决定是否继续,那么它就不再是foldl了。


请问您能否回答一个哲学上的问题:“在不设计它的情况下,打破折叠循环是否可行?”哪个更好的选择:1)发明一些东西(打破折叠循环)2)编写更多代码(进行递归)? - ravnur
我认为这很好。throw/catch 几乎没有额外的开销(肯定比每个函数应用程序结果中带有继续或不继续标志的类似折叠的函数要少),它清晰易读,并且您使用了常见的习语(foldl),而不是编写自己的递归函数。 - RichardC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接