Unexpected result while reversing a list

自闭症网瘾萝莉.ら 提交于 2019-12-22 04:19:22

问题


I need some explanation into the unexpected result of the code below, seemingly, due to some bug.

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]

Is this because of some bug?


回答1:


When you get a totally unexpected result, especially with relatively simple function like this, it can be helpful to follow the logic through by hand. So let's see what happens here:

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 ==>
...

You can see where this is going. The problem is simple; you're just reversing the wrong part of the list in the recursive step. Instead of reversing the tail like you're doing now, you want to reverse everything but the last element. So you could revise it to something like this:

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

which returns what you'd expect: reverse' [1,91,99,20,1,6,5,2,8,0] = [0,8,2,5,6,1,20,99,91,1]




回答2:


As others already pointed out the mistake, let me show you a useful and elegant technique which can be often applied in such cases and leads often to efficient algorithms: Use an accumulator.

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

So you have a sub-function with an additional argument (the "accumulator") which collects what you already have. Obviously in the base case you need to give this result back, because you are done. The recursive case is very simple here: It's just like having a stack of plates, which you take one by one from the top, and build a new stack by adding one by one at the top. This resulting stack is reversed, just like we need it.

Note that for some other applications of this technique you don't want that reversion, which can be fixed by inserting a reverse in the base case.




回答3:


The minimal correction to your original code is arguably

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

i.e. you were combining your list's sub-parts in a wrong order.

This is of course still a quadratic algorithm. You get an iterative version out of it by first observing

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

and then regrouping and keeping thus formed intermediate results in a separate, accumulator argument, as was pointed out in a previous response:

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

Having found your workhorse, you set it up with an appropriate interface,

reverse' xs = rev xs []

(plus few edge cases).




回答4:


While playing around with Data.Monoid I found the following alternative, slightly rube-goldbergish solution:

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))


来源:https://stackoverflow.com/questions/8117057/unexpected-result-while-reversing-a-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!