如何在 Haskell 中有效地通过foldr 编写反向操作?

发布于 2024-12-11 04:10:25 字数 257 浏览 0 评论 0原文

请注意,由于复杂性呈二次方增长,平凡的解决方案

reverse a = foldr (\b c -> c ++ [b] ) [] a

效率不高。如果尝试使用通常的foldl 到foldr 转换(盲目地),但我的尝试

foldr (\b g x -> g ((\x old -> x:old) x b)) id list []

没有按我的预期工作。

Note that the trivial solution

reverse a = foldr (\b c -> c ++ [b] ) [] a

is not very efficient, because of the quadratic growth in complexity. If have tried to use the usual foldl to foldr conversion (blindly), but my attempt

foldr (\b g x -> g ((\x old -> x:old) x b)) id list []

did not work as I expected.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

祁梦 2024-12-18 04:10:25

试试这个:

reverse bs = foldr (\b g x -> g (b : x)) id bs []

虽然通常使用

reverse = foldl' (flip (:)) []

Try this:

reverse bs = foldr (\b g x -> g (b : x)) id bs []

Though it's usually really better to write it using foldl':

reverse = foldl' (flip (:)) []
无可置疑 2024-12-18 04:10:25

考虑以下内容:

foldr (<>) seed [x1, x2, ... xn] == x1 <> (x2 <> (... <> (xn <> seed)))

让我们将其“切割”成碎片:

(x1 <>) (x2 <>) ... (xn <>)  seed

现在我们有了这堆函数,让我们组合它们:

(x1 <>).(x2 <>). ... .(xn <>).id $ seed

((.), id) 它是 Endo 幺半群,所以

foldr (<>) seed xs == (appEndo . foldr (mappend.Endo.(<>)) mempty $ xs) seed

对于左折叠,我们只需要 Dual 幺半群。

leftFold (<>) seed xs = (appEndo . getDual . foldr (mappend . Dual . Endo . (<>)) mempty $ xs) seed

(<>) = (:)seed = []

reverse' xs = (appEndo . getDual . foldr (mappend . Dual . Endo . (:)) mempty $ xs) []

或者简单:

reverse' xs = (appEndo . foldr (flip mappend . Endo . (:)) mempty $ xs) []
reverse' xs = (foldr (flip (.) . (:)) id $ xs) []
reverse' = flip (foldr (flip (.) . (:)) id) []

Consider the following:

foldr (<>) seed [x1, x2, ... xn] == x1 <> (x2 <> (... <> (xn <> seed)))

Let's just "cut" it into pieces:

(x1 <>) (x2 <>) ... (xn <>)  seed

Now we have this bunch of functions, let's compose them:

(x1 <>).(x2 <>). ... .(xn <>).id $ seed

((.), id) it's Endo monoid, so

foldr (<>) seed xs == (appEndo . foldr (mappend.Endo.(<>)) mempty $ xs) seed

For left fold we need just Dual monoid.

leftFold (<>) seed xs = (appEndo . getDual . foldr (mappend . Dual . Endo . (<>)) mempty $ xs) seed

(<>) = (:) and seed = []

reverse' xs = (appEndo . getDual . foldr (mappend . Dual . Endo . (:)) mempty $ xs) []

Or simple:

reverse' xs = (appEndo . foldr (flip mappend . Endo . (:)) mempty $ xs) []
reverse' xs = (foldr (flip (.) . (:)) id $ xs) []
reverse' = flip (foldr (flip (.) . (:)) id) []
倒带 2024-12-18 04:10:25

基本上,您需要将 1:2:3:[] 转换为 (3:).(2:).(1:) 并将其应用于 []。因此:

reverse' xs = foldr (\x g -> g.(x:)) id xs []

这里累积的 g 的含义是,它通过将 xs 的反向部分尾部附加到它的参数上来作用于它的参数。

对于 1:2:3:[] 示例,在最后一步中 x 将是 3,g 将是 (2:).(1:)。

Basically, you need to transform 1:2:3:[] into (3:).(2:).(1:) and apply it to []. Thus:

reverse' xs = foldr (\x g -> g.(x:)) id xs []

The meaning of the accumulated g here is that it acts on its argument by appending the reversed partial tail of xs to it.

For the 1:2:3:[] example, in the last step x will be 3 and g will be (2:).(1:).

情仇皆在手 2024-12-18 04:10:25
foldl (\acc x -> x:acc) [] [1,2,3]
foldl (\acc x -> x:acc) [] [1,2,3]
阪姬 2024-12-18 04:10:25

老问题,我知道,但是这种方法有什么非最佳的吗,由于惰性求值,foldr 似乎会更快,并且代码相当简洁:

 reverse' :: [a] -> [a]
 reverse' = foldr (\x acc -> acc ++ [x]) []

(++) 明显慢于 (:),这需要FUZxxl 的回答中显示了一些逻辑上的扭曲

old question, I know, but is there anything non-optimal about this approach, it seems like foldr would be faster due to lazy evaluation and the code is fairly concise:

 reverse' :: [a] -> [a]
 reverse' = foldr (\x acc -> acc ++ [x]) []

is (++) significantly slower than (:), which requires a few logical twists as shown in FUZxxl's answer

囍笑 2024-12-18 04:10:25

reverse = Foldl(\ ab -> b :a) []

reverse = foldl(\ a b -> b :a) []

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文