【问题标题】:Unexpected result while reversing a list反转列表时出现意外结果
【发布时间】:2011-11-14 03:41:14
【问题描述】:

我需要对下面代码的意外结果进行一些解释,似乎是由于一些错误。

reverse' :: [b] -> [b]
reverse' [] = []
reverse' [x] = [x]
reverse'(x:xs) = last (x:xs) : reverse' xs

*Main> reverse' [0,8,2,5,6,1,20,99,91,1]
[1,1,1,1,1,1,1,1,1,1]

这是因为一些错误吗?

【问题讨论】:

  • 问题是您总是选择相同的最后一个元素作为列表的头部。快速修复是reverse' xs = last xs : reverse' (init xs)。然而这个算法太可怕了:)
  • 感谢 is7s。我得更仔细地阅读我的代码

标签: list haskell reverse


【解决方案1】:

当你得到一个完全出乎意料的结果时,尤其是像这样相对简单的函数,手动遵循逻辑会很有帮助。那么让我们看看这里发生了什么:

reverse' (0:[8,2,5,6,1,20,99,91,1]) = 1 : reverse' xs ==>
1 : (reverse' (8:[2,5,6,1,20,99,91,1]) = 1 : reverse' xs ==>
1 : 1 : (reverse' (2:[5,6,1,20,99,91,1]) = 1 : reverse' xs ==>
...

你可以看到这是怎么回事。问题很简单;您只是在递归步骤中反转列表的错误部分。不是像现在这样反转尾部,而是想要反转除最后一个元素之外的所有内容。所以你可以把它修改成这样:

reverse' :: [b] -> [b]
reverse' [] = []
reverse' [x] = [x]
reverse' xs = last xs : reverse' (init xs)

返回你所期望的:reverse' [1,91,99,20,1,6,5,2,8,0] = [0,8,2,5,6,1,20,99,91,1]

【讨论】:

  • 天哪,非常感谢。这是一个非常愚蠢的“cs101”错误!
  • 然而,正如 is7s 所说,reverse' xs = last xs : reverse' (init xs) 只是一个快速修复。算法是O(n²),这对于反转列表来说是荒谬的。
  • 是的,这是真的。如果您想知道 Prelude 中的版本是如何实现它的,您可以查看here。
【解决方案2】:

正如其他人已经指出的错误,让我向您展示一种有用且优雅的技术,该技术通常可以应用于这种情况并通常导致高效的算法:使用累加器。

rev xs = rev' xs [] where
  rev' [] acc = acc
  rev' (x:xs) acc = rev' xs (x:acc) 

所以你有一个带有附加参数(“累加器”)的子函数,它收集你已经拥有的东西。显然,在基本情况下,您需要返回此结果,因为您已经完成了。这里的递归情况很简单:就像有一堆盘子,你从顶部一个接一个地取出,然后通过在顶部一个接一个地添加来构建一个新的堆栈。这个结果堆栈被反转了,就像我们需要它一样。

请注意,对于此技术的其他一些应用程序,您不希望这种反转,可以通过在基本情况下插入 reverse 来解决。

【讨论】:

    【解决方案3】:

    可以说对原始代码的最小修正是

    -- reverse'(x:xs) = last (x:xs) : reverse' xs
    reverse' (x:xs) = reverse' xs ++ [x]
    

    即您以错误的顺序组合列表的子部分。

    这当然还是一个二次算法。你可以通过首先观察得到一个迭代版本

    reverse' (a:b:c:d:xs) = (((reverse' xs ++ [d]) ++ [c]) ++ [b]) ++ [a]
    

    然后重新组合并保留由此形成的中间结果,生成一个单独的 累加器 参数,正如之前的回复中所指出的那样:

    rev (x:xs) acc = rev xs (x:acc)
    

    找到您的主力后,您可以使用适当的界面对其进行设置,

    reverse' xs = rev xs []
    

    (加上一些边缘情况)。

    【讨论】:

    • 通过等式推理稍加阐述:reverse' (a:b:c:d:xs) = (((reverse' xs ++ [d]) ++ [c]) ++ [b]) ++ [a] = reverse' xs ++ ([d] ++ ([c] ++ ([b] ++ [a]))) = reverse' xs ++ (d:c:b:a:[]) = reverse' (d:xs) ++ (c:b:a:[]),我们通过搁置 (++) 操作得到累加器。
    【解决方案4】:

    在玩Data.Monoid 时,我发现了以下替代方案,略带粗俗的解决方案:

    import Data.Foldable
    import Data.Monoid
    
    reverse = getDual . foldMap (Dual . (:[])) 
    

    【讨论】:

      【解决方案5】:
      reverse2 :: [a] -> [a]
      reverse2 [] = []
      reverse2 [x] = [x]
      reverse2 x = (last x) : (reverse2 (init x))
      

      【讨论】:

      • 你能解释一下吗?为什么你的答案比已经发布的更好?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-06
      • 1970-01-01
      • 2018-07-18
      • 2013-11-07
      • 2015-05-30
      • 1970-01-01
      • 2014-04-22
      相关资源
      最近更新 更多