我正在尝试撤销列表。
以下是我的代码:
reverseList :: [Int] -> [Int]
reverseList [] = []
reverseList (x:xs) = x:reverseList xs
最终发生的事情是我最终以相同的顺序返回列表。我甚至有一个如何扭转列表的解决方案,但我想知道我在这里做错了什么?我对haskell很新,所以我认为我应该专注于理解更多,然后我可以轻松解决更多问题。我知道有很多解决方案可以解决这个问题,但我需要更多的帮助来理解我在这段代码中做错了什么。
答案 0 :(得分:39)
在Haskell中有几种方法可以解决这个问题。天真的方法是使用连接函数++
:
reverseList [] = []
reverseList (x:xs) = reverseList xs ++ [x]
然而,对于大型列表来说这将非常慢,因为Haskell列表实际上是单链表,因此为了附加元素,您必须遍历整个列表。另一种方法是跟上你在辅助函数中构建的列表:
reverseList = go []
where
go acc [] = acc
go acc (x:xs) = go (x:acc) xs
但是,这实际上只是fold
模式:
reverseList = foldl (\acc x -> x : acc) []
但\acc x -> x : acc
只是flip (:)
,因此可以写成
reverseList = foldl (flip (:)) []
然而,最简单的方法可能是在Prelude中使用reverse
函数。
我想指出您的reverseList :: [Int] -> [Int]
类型可以推广到:: [a] -> [a]
,您不会对列表中的元素做任何特殊处理,您只需要构建一个新列表和他们一起。
答案 1 :(得分:5)
您将列表分为头部和尾部,但随后以相同的顺序重新组合列表。以列表[1, 2, 3]
为例:
在第一次通话中,x
将为1
,xs
将为[2, 3]
。然后,您创建一个新列表,前面是x
(so 1),后跟reverseList [2, 3]
。
答案 2 :(得分:3)
在Haskell中有几种方法可以解决此问题。这是一个使用cons和last / init的解决方案:
reverseList [] = []
reverseList xs = last xs : reverseList (init xs)
或使用折叠架:
reverseList xs = foldl (\x y -> y:x) [] xs
答案 3 :(得分:0)
基本上是使用追加的天真的算法
revList [] = []
revList (x:xs) = revList xs ++ [x]
效率低下,因为附加是O(n)
操作,其中n
是++
运算符的第一个(左)参数的长度。因此,上面的revList
函数结果为O(n(n-1)/ 2)〜O(n ^ 2)。
因此,对于此类附加繁重的任务,存在差异列表数据类型。
差异列表只是表示为功能的列表。我的意思是,当以DList类型表示时,像[1,2,3]
这样的列表将是\xs -> [1,2,3] ++ xs
或简称为([1,2,3] ++)
type DList a = [a] -> [a]
toDList :: [a] -> DList a
toDList xs = (xs ++ )
fromDList :: DList a -> [a]
fromDList f = f []
这很酷,因为因为DList是函数,所以我们可以通过composition(。)运算符将它们附加并获得新的DList。换句话说,toDList (xs ++ ys) == (toDList xs) . (toDList ys)
。
那么这有什么用?通过使用嵌套的函数组合,我们可以以类似于revList
函数的方式来反转列表,但这将使我们花费更少。因为每个函数的组成都是O(1),所以只有O(n)。
revList' :: [a] -> DList a
revList' [] = toDList []
revList' (x:xs) = revList' xs . toDList [x]
现在我们将[a]
中的DList a
颠倒了,我们需要应用fromDList
fastReverse :: [a] -> [a]
fastReverse = fromDList . revList'
差异列表数据类型不像我上面显示的那么简单。它可以具有Monoid,Functor和MonadT实例。有关此有用数据类型的更多信息,请检查Data.DList
答案 4 :(得分:0)
简单;使用内置的reverse
函数:
print (reverse [1,2,3,4,5]) -- [5,4,3,2,1]