【问题标题】:How does this foldl-based function work: "myreverse = foldl (flip (:)) []"?这个基于 foldl 的函数是如何工作的:“myreverse = foldl (flip (:)) []”?
【发布时间】:2015-05-21 09:36:23
【问题描述】:

我正在学习haskell,我尝试在不使用递归的情况下编写自己的反向函数。

解决方法是这个函数:

myreverse = foldl (flip (:)) []

我试图了解在评估过程中会发生什么,比如:

myreverse [1..5]

我不明白翻转在这里做什么。有人可以用分步说明写下这里发生的事情吗?

【问题讨论】:

  • 我无法理解 flip 在这里做了什么。(:) 写成 lambda:\x xs -> x : xs 可能会对您有所帮助。如果将flip 应用于此 lambda,则生成的函数为 \xs x -> x : xs
  • protip:使用 List 模块中的 foldl' 而不是 foldl。普通的foldl 永远不是最好的选择(除非你正在解决一些crazy made up problem
  • @hugomg,我认为在这种情况下应该没问题。 flip (:) 不进行任何实际计算,因此我希望 GHC 将其视为构造函数。不过,您必须进行测试才能确定。

标签: haskell recursion foldleft


【解决方案1】:

翻转很简单:

如果你有一个函数f :: a -> b -> c,那么flip f 就是一个函数:: b -> a -> c,所以它会返回一个新函数并翻转你必须传递的参数的顺序。

(:) :: a -> [a] -> [a] 是这种模式的一个函数,所以flip (:) 现在将是一个函数,它首先获取即将成为尾部的函数,然后是新的头部,并返回一个包含这些的新列表:

(flip (:)) [2..4] 1
= (:) 1 [2..4]
= 1 : [2..4]
= [1,2,3,4]

现在你需要这个,因为foldl 是这样定义的:

foldl :: (b -> a -> b) -> b -> [a] -> b

你看 - 你必须传递的函数将是一个首先接受一个列表,然后是一个元素并返回一个新列表的函数

现在这将折叠成这样的:

myreverse [1..5]
= foldl (flip (:)) [] [1,2,3,4,5]
= foldl (flip (:)) (((flip (:)) [] 1)) [2,3,4,5]
= foldl (flip (:)) (1:[]) [2,3,4,5]
= foldl (flip (:)) (((flip (:)) [1] 2)) [3,4,5]
= foldl (flip (:)) (2:[1]) [3,4,5]
= ...
= [5,4,3,2,1]

【讨论】:

  • 小反对:foldl 不会导致范围从头开始完全评估。
  • 当然是的 - 但我认为如果我写下序列并注意所有懒惰的部分,这将变得非常混乱并且几乎无法阅读 - 我希望缩进和方式 flip @ 987654332@ 和 foldl 在这里工作这样会更明显
  • 是的,是的,没问题。毕竟,你写了“类似这样的东西”。
【解决方案2】:

您可以在 ghci 中尝试 flip 的作用:

:t (:)
(:) 1 [2..4]

[1,2,3,4]

:t flip (:)
(flip (:)) [2..4] 1

[1,2,3,4]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-27
    • 1970-01-01
    • 2021-12-20
    • 1970-01-01
    • 2012-01-04
    • 2013-01-17
    相关资源
    最近更新 更多