【发布时间】:2010-11-27 06:03:45
【问题描述】:
prefixes ls = zipWith take [1 .. length ls] (repeat ls)
有没有比这更好的方法?直观地说,在我看来,在纯函数式语言中无法获得低于 O(n²) 的算法,因为必须应用反向或追加 n 次。不过,我不知道如何证明这一点。
【问题讨论】:
标签: algorithm haskell functional-programming performance
prefixes ls = zipWith take [1 .. length ls] (repeat ls)
有没有比这更好的方法?直观地说,在我看来,在纯函数式语言中无法获得低于 O(n²) 的算法,因为必须应用反向或追加 n 次。不过,我不知道如何证明这一点。
【问题讨论】:
标签: algorithm haskell functional-programming performance
我认为你是对的。不能共享列表的脊椎,因为所有尾部都不同。因此,如果完全评估前缀列表,将占用完整的 Θ(n2) 空间,这必须花费 Ω(n2) 时间来生成。
请注意,您编写的函数(较懒的版本)在Data.List 中作为inits 可用。
你可以做一个巧妙的优化。这个等式成立:
map (foldl f z) . inits = scanl f z
而scanl 以线性时间运行。因此,如果您可以将要对每个前缀执行的操作表述为左折叠,那么您可以避免构建前缀列表的二次复杂度。
【讨论】:
这不是依赖于表示吗?如果您将列表表示为连续存储加上开始和结束索引(类似于字节串),您可以共享存储并且只需要遍历一次即可构建索引列表。算法不会改变,只是表示。对于这个特定的用例,使用 snoc 列表(二进制列表,但从列表的末尾而不是开头嵌套)也允许共享子列表,对吗?
【讨论】:
[]。 -1