如何在Haskell中有效地编写foldr的反向代码?

如何在Haskell中有效地编写foldr的反向代码?,haskell,Haskell,请注意,平凡的解决方案 reverse a = foldr (\b c -> c ++ [b] ) [] a 由于复杂性的二次增长,效率不是很高。如果我尝试使用通常的foldl到foldr转换(盲目),但是我的尝试 foldr (\b g x -> g ((\x old -> x:old) x b)) id list [] 没有按我预期的那样工作。请尝试以下操作: reverse bs = foldr (\b g x -> g (b : x)) id bs [] 虽

请注意,平凡的解决方案

reverse a = foldr (\b c -> c ++ [b] ) [] a
由于复杂性的二次增长,效率不是很高。如果我尝试使用通常的foldl到foldr转换(盲目),但是我的尝试

foldr (\b g x -> g ((\x old -> x:old) x b)) id list []
没有按我预期的那样工作。

请尝试以下操作:

reverse bs = foldr (\b g x -> g (b : x)) id bs []
虽然通常使用以下方法编写会更好:


考虑以下几点:

foldr (<>) seed [x1, x2, ... xn] == x1 <> (x2 <> (... <> (xn <> seed)))
对于左折叠,我们只需要
Dual
monoid

leftFold (<>) seed xs = (appEndo . getDual . foldr (mappend . Dual . Endo . (<>)) mempty $ xs) seed
或者很简单:

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

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

reverse' xs = foldr (\x g -> g.(x:)) id xs []
这里累加的g的意思是,它通过附加xs的反向部分尾来作用于它的参数


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

我知道这是一个老问题,但这种方法是否有任何非最佳的地方,由于延迟求值,foldr似乎会更快,代码也相当简洁:

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

是(++)比(:)慢得多,这需要一些逻辑上的曲折,如fuzzxl的回答所示

Chris:让我们说一下:累加器的类型是[a]->[a]。它基本上构建了许多封装的lambda,将列表的元素添加到所传递内容的前面-[]。@Chris我个人更喜欢用无点表示法,这样(对我)更可读:
reverse xs=foldr f id xs[]其中f x r=r。(x:)
。因此,当
foldr f id xs
的结果最终应用于
[]
时,调用
f x1 r1
,从而生成
r1。(x1:)$[]
此时强制执行
r1
。最后,整个链
id.(xn:)。(x2:)(x1:)
应用于
[]
。这使用标准的Haskell方法将开放式(也称为“差异-”)列表编码为列表生成函数链,
:[a]->[a]
。在
数据中也使用了同样的方法。我相信序列
。这是
foldl'
不好的少数情况之一。盲目应用惰性构造函数的行为永远不会延迟,因为这样做没有任何好处。如果产生的代码实际上是不同的,那么情况只会更糟。答案的位置不对,但正是我所寻找的。我想说的是,很久以前我就意识到我的答案不正确,但令我惊讶的是,很多人已经投了赞成票(我总共获得了5票赞成票和5票反对票)。我想有时候人们在寻找这个答案时会登陆到这个页面,因此我决定把它保留在这里,因为最终它都是为了帮助其他人找到他们正在寻找的答案。这个实现在列表的长度上是O(n^2)。在每个步骤中,当前的
acc
都会变成垃圾,并创建一个新列表,其中
x
作为最后一个元素。惰性评估仅在发生这种情况时才会更改。在严格的语言中,它会发生在
reverse'
返回之前。在Haskell中,强制执行结果列表时会发生这种情况。啊,这很有道理。没有想到在每次迭代时都会创建一个新列表。谢谢一些静态可用的
++
应用程序可以立即推出,但在这里它们实际上都会出现,您将为此付出高昂的代价。
leftFold (<>) seed xs = (appEndo . getDual . foldr (mappend . Dual . Endo . (<>)) mempty $ xs) 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) []
foldl (\acc x -> x:acc) [] [1,2,3]
reverse' xs = foldr (\x g -> g.(x:)) id xs []
 reverse' :: [a] -> [a]
 reverse' = foldr (\x acc -> acc ++ [x]) []