【问题标题】:fail to use foldl to reverse a list in Haskell无法使用 foldl 来反转 Haskell 中的列表
【发布时间】:2017-09-02 09:12:47
【问题描述】:

我知道如何使用递归来反转列表,但我正在尝试使用 foldl 来提高效率。我的代码如下:

reverse list = foldl (++) [] (map (\x -> [x]) list)

在 GHCi 中运行它时,它返回与输入相同的列表。出了什么问题?我也尝试通过 foldr 完成它,但它没有显示任何变化。

【问题讨论】:

  • Reverse a list in haskell的可能重复
  • @icc97 虽然在那个问题中给出了正确的foldl 解决方案,但问题本身并不是关于这个的,所以我不会说它是重复的。
  • @leftaroundabout OP 想要使用foldltop answer 的列表反转为重复问题给出的列表:reverseList = foldl (\acc x -> x : acc) []
  • 正如我所说,解决方案肯定存在。只是,一个 question duplicate 实际上应该问同样的问题,并且那里并没有真正解决为什么 foldl 没有 flip 会以错误的顺序给出结果。

标签: list haskell fold


【解决方案1】:

foldl 将累加器作为第一个参数传递给函数。 ++ 将第一个参数连接到第二个参数,但是,要反转列表,您需要将第二个参数连接到第一个参数。您可以使用flip (++)

Prelude> let reverse list = foldl (flip (++)) [] (map (\x -> [x]) list)
Prelude> reverse [1..5]
[5,4,3,2,1]

【讨论】:

  • "concatenates ... to" 是非定向的;令人困惑。 “之前”会好很多。
【解决方案2】:

您可以使用 cons (:) 函数,而不是首先将列表的所有元素转换为单例列表(这也很昂贵):

reverse :: Foldable t => t a  -> [a]
reverse = foldl (flip (:)) []

所以这里我们使用flip (:) :: [a] -> a -> [a] 作为折叠函数。它需要一个尾部[a] 和一个头部a,并构造一个以头部为第一个元素,尾部为最后一个元素的列表。

那么会发生什么:

   foldl (flip (:)) [] [1,4,2,5]
-> foldl (flip (:)) (1:[]) [4,2,5]
-> foldl (flip (:)) (4:1:[]) [2,5]
-> foldl (flip (:)) (2:4:1:[]) [5]
-> foldl (flip (:)) (5:2:4:1:[]) []
-> (5:2:4:1:[])
-> [5,2,4,1]

【讨论】:

  • 我一直在为 foldl 与 foldr 的区别而苦苦挣扎,为什么在这里使用 foldl?你介意快速解释一下吗?谢谢!
  • @DanielSanchez:foldr 开始列表的右端(或更一般的可折叠)。所以如果列表是[a,b,c,d],它将计算f a (f b (f c (f d z)))。而foldl 将计算f (f (f (f z a) b) c) d。使用z 初始元素`。
  • 为我工作!谢谢你的建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-18
  • 1970-01-01
  • 2015-01-06
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
相关资源
最近更新 更多