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

shu*_*alo 10 haskell

注意那个简单的解决方案

reverse a = foldr (\b c -> c ++ [b] ) [] a
Run Code Online (Sandbox Code Playgroud)

由于复杂性的二次增长,效率不高.如果试图使用通常的foldl来折叠转换(盲目),但我的尝试

foldr (\b g x -> g ((\x old -> x:old) x b)) id list []
Run Code Online (Sandbox Code Playgroud)

没有像我预期的那样工作.

fuz*_*fuz 21

试试这个:

reverse bs = foldr (\b g x -> g (b : x)) id bs []
Run Code Online (Sandbox Code Playgroud)

虽然使用foldl'编写它通常会更好:

reverse = foldl' (flip (:)) []
Run Code Online (Sandbox Code Playgroud)

  • @Chris我个人更喜欢它的无点符号,它对我来说更具可读性:`反向xs = foldr f id xs []其中fxr = r.(X:)`.因此,当`foldr f id xs`的结果最终应用于`[]`时,调用`f x1 r1`,产生`r1.(x1 :) $ []`此时`r1`被强制执行.最后整个链`id.(xn :).......(x2:).(x1:)`适用于`[]`.这使用标准的Haskell方法将开放式(又名*"差异 - "*)列表编码为列表生成函数链,`:: [a] - > [a]`.我相信`Data.Sequence`中使用的是相同的. (5认同)
  • 克里斯:我们只说:累加器的类型为[a] - > [a].它基本上构建了很多封装的lambda,它将列表的元素添加到传递的内容 - []. (2认同)

kla*_*ius 9

考虑以下:

foldr (<>) seed [x1, x2, ... xn] == x1 <> (x2 <> (... <> (xn <> seed)))
Run Code Online (Sandbox Code Playgroud)

让我们把它"切"成碎片:

(x1 <>) (x2 <>) ... (xn <>)  seed
Run Code Online (Sandbox Code Playgroud)

现在我们有了这一系列功能,让我们来组合它们:

(x1 <>).(x2 <>). ... .(xn <>).id $ seed
Run Code Online (Sandbox Code Playgroud)

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

foldr (<>) seed xs == (appEndo . foldr (mappend.Endo.(<>)) mempty $ xs) seed
Run Code Online (Sandbox Code Playgroud)

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

leftFold (<>) seed xs = (appEndo . getDual . foldr (mappend . Dual . Endo . (<>)) mempty $ xs) seed
Run Code Online (Sandbox Code Playgroud)

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

reverse' xs = (appEndo . getDual . foldr (mappend . Dual . Endo . (:)) mempty $ xs) []
Run Code Online (Sandbox Code Playgroud)

或者简单:

reverse' xs = (appEndo . foldr (flip mappend . Endo . (:)) mempty $ xs) []
reverse' xs = (foldr (flip (.) . (:)) id $ xs) []
reverse' = flip (foldr (flip (.) . (:)) id) []
Run Code Online (Sandbox Code Playgroud)