由于某些错误,我需要对下面代码的意外结果进行一些解释。
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]
这是因为某些错误吗?
答案 0 :(得分:17)
当你得到一个完全出乎意料的结果时,尤其是像这样相对简单的功能,手动跟踪逻辑会很有帮助。那么让我们看看这里发生了什么:
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]
答案 1 :(得分:11)
正如其他人已经指出这个错误一样,让我向您展示一种有用且优雅的技术,这种技术经常适用于这种情况,并且通常会导致有效的算法:使用累加器。
rev xs = rev' xs [] where
rev' [] acc = acc
rev' (x:xs) acc = rev' xs (x:acc)
所以你有一个带有附加参数的子功能(“累加器”),它收集你已经拥有的东西。显然,在基本情况下,你需要回复这个结果,因为你已经完成了。递归的情况在这里非常简单:它就像有一叠盘子,你从顶部一个接一个地拿走,并通过在顶部逐个添加来构建一个新的堆栈。这个结果堆栈是相反的,就像我们需要它一样。
请注意,对于此技术的某些其他应用程序,您不希望通过在基本情况下插入reverse
来修复此版本。
答案 2 :(得分:6)
对原始代码的最小修正可以说是
-- 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 []
(加上少数边缘案例)。
答案 3 :(得分:2)
在玩Data.Monoid
的过程中,我找到了以下替代方案,稍微有点红润的解决方案:
import Data.Foldable
import Data.Monoid
reverse = getDual . foldMap (Dual . (:[]))
答案 4 :(得分:0)
reverse2 :: [a] -> [a]
reverse2 [] = []
reverse2 [x] = [x]
reverse2 x = (last x) : (reverse2 (init x))