【问题标题】:Most efficient way of building a list in a left fold?在左折叠中构建列表的最有效方法?
【发布时间】:2016-04-18 14:26:25
【问题描述】:

在构建列表时,我通常使用右折叠,因为这样我可以使用右关联 : 运算符,而不会影响结果列表的顺序。在左折叠中,我可以使用++,但我知道这将需要在生成列表时重复复制列表,从而为 N 元素列表提供 O(N^2) 操作,这通常是不可接受的。

所以,当我必须使用左折叠时(在我的特定情况下,这是因为我使用 foldlM 并且产生的单子动作必须按从左到右的顺序执行),除了使用: 在折叠中构建列表并反转结果之外,还有什么更好的方法可以解决这个问题?

【问题讨论】:

  • 在较新的版本中,Data.Foldable.foldl 被定义为foldr,以便与其他操作融合。 reverse 应该是其中之一,因此您应该只使用 reverse $ foldl ... 就可以得到一个非常高效的版本。至少我的猜测。
  • 不,反向不参与列表融合。

标签: haskell functional-programming fold


【解决方案1】:

当我必须使用左折叠时(......因为......产生的一元动作必须按从左到右的顺序执行)

右折叠可以向右倾斜到再次向左折回。例如,您可以打印(即单子操作)每个数字并使用 右折叠计算列表从从左到右的部分总和:

fun :: [Int] -> IO [Int]
fun xs = foldr go (const $ return []) xs 0
    where go x f a = let a' = a + x in print x >> (a' :) <$> f a'

然后:

\> fun [1..5]
1
2
3
4
5
[1,3,6,10,15]

请注意,输出列表是使用(a' :) 构建的,并且单子操作是从左到右执行的,即使它是右折叠。

【讨论】:

    【解决方案2】:

    既然你提到了foldlM,我的这篇博文很可能会深入回答你的问题:

    Constructing a list in a Monad

    底线是:使用差异列表;即在您的左折叠中,您累积了一个[a] -&gt; [a] 类型的值,您可以在其中有效地使用. (x:) 附加一个值,最后您将此函数应用于[] 以获得您的列表。

    【讨论】:

    • 差异列表是否比累积列表然后反转它更好?
    • 啊,我猜你是这样发现的。我很惊讶;我已经看到逆转技术在其他情况下获胜(稍微)。整个情况有些不尽人意。我希望看到一个安全的IO 交织组合器,它允许IO 懒惰地产生一个值,但不允许 允许懒惰的评估来驱动IO。一种lazyFixIO
    • 我同意; “这是一个有洞的表达式,我稍后会填补这个洞”的组合器会很好。但无论如何,这将是对某个闭包的可变更改,运行时/GC 总是会对其进行惩罚。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    • 2017-04-12
    • 1970-01-01
    • 2021-12-03
    • 2011-12-19
    • 1970-01-01
    相关资源
    最近更新 更多