List Haskell中的所有列表旋转
我有一个旋转列表的功能:List Haskell中的所有列表旋转,list,haskell,functional-programming,List,Haskell,Functional Programming,我有一个旋转列表的功能: rotate :: [a] -> [a] rotate [] = [] rotate (x:xs) = xs ++ [x] 现在我想要一个函数,它给出一个列表,其中包含有限列表的每一个可能的旋转: rotateAll :: [a] -> [[a]] 在命令式语言中,我会做一些类似(伪代码)的事情 当然,强制思考可能无助于我找到解决这个问题的功能性解决方案。我正在寻找一些关于如何解决这个问题的提示 其他想法: 为了解决这个问题,我有两个问题要解决。首先,我
rotate :: [a] -> [a]
rotate [] = []
rotate (x:xs) = xs ++ [x]
现在我想要一个函数,它给出一个列表,其中包含有限列表的每一个可能的旋转:
rotateAll :: [a] -> [[a]]
在命令式语言中,我会做一些类似(伪代码)的事情
当然,强制思考可能无助于我找到解决这个问题的功能性解决方案。我正在寻找一些关于如何解决这个问题的提示
其他想法:
为了解决这个问题,我有两个问题要解决。首先,我需要反复旋转一个列表,并将每个结果收集到一个列表中。因此,第一个解决方案需要执行以下操作
rotateAll xs = [xs (rotate xs) (rotate (rotate xs)) (rotate (rotate (rotate xs))) ...]
当然我不知道要做多少次。我可以无限地这样做,然后使用take(length xs)
来获得我想要的有限数量的列表。这实际上说明了第二个问题:确定何时停止。我不知道使用take
是否是解决此问题的最有效或最优雅的方法,但我在键入此内容时想到了这一点,并且应该可以工作
附录:
现在,我已经找到了两个解决方案,一个是我自己的,另一个是有提示的。我将很高兴地欢迎任何其他更快或使用不同方法的解决方案。谢谢 除此之外,您还可以编写一个函数来生成n个旋转的列表。案例n=0将把输入包装在一个列表中,案例n=m+1将把输入附加到案例m的结果中。虽然通常首选使用标准函数,但有时编写自己的解决方案是有益的。我提出了两种解决方案。第一个是在我发布问题后出现的手工制作的:
rotateAll :: [a] -> [[a]]
rotateAll xs = rotateAll' xs (length xs)
where rotateAll' xs 1 = [xs]
rotateAll' xs n = xs : rotateAll' (rotate xs) (n - 1)
另一个使用@Tilo的建议:
rotateAll :: [a] -> [[a]]
rotateAll xs = take (length xs) (iterate rotate xs)
你可能也想考虑这个网站回答同样的问题。
由于注释而编辑:基于
旋转的实现以及基于初始化的实现和尾部的实现应该是列表长度的二次方。但是,基于inits
和tails
的方法效率应该较低,因为它执行多个二次遍历。但是请注意,只有在完全评估结果时,这些语句才有效。此外,编译器可能能够改进代码,因此您必须谨慎对待这些语句。您也可以递归生成它们。生成空列表或单个元素列表的所有旋转非常简单,而生成x:xs:xs的旋转则是将x
插入xs
所有旋转的正确位置
您可以通过生成要插入的索引(假设前面的旋转是按此顺序进行的,只需列表[1,2,…])并使用zipWith
将x
插入到正确的位置来完成此操作
或者,您可以使用inits
和tails
组合来拆分
围绕位置的旋转,并使用zipWith
将它们粘在一起。使用数据中的预定义函数。List
!您可以使用四个函数调用、无递归和无rotate
函数获得所有旋转的列表
您要求不要在此处发布完整的解决方案。对于那些想看到它的人,一个完整的解决方案(一行代码)出现在
附录:现在,我已经找到了两个解决方案,或者是我自己的,或者是有提示的。
我将很高兴地欢迎任何其他更快或更实用的解决方案
不同的方法。谢谢
由于没有其他人指出使用cycle
我想我会为有限列表添加此解决方案:
rotations x = let n = length x in map (take n) $ take n (tails (cycle x))
对于无限列表x
,旋转只是尾部x
计算循环x
是O(n)时间和空间,尾部的每个元素是O(1),take n
是O(n)时间和空间,但这两个take n
是嵌套的,因此计算整个结果是O(n^2)时间和空间
如果它在惰性地生成下一个旋转之前垃圾收集每个旋转,那么空间理论上是O(n)
如果你对自己的消费量很了解,那么你就不需要map(take n)
,只需走循环x
或take n(tails(cycle x))
并保留空间O(n)。这是一个在旋转列表本身和列表中的每个条目上都完全懒惰的版本。关键在于,与其预先计算长度,不如在运行时将结果中的元素与列表中的元素进行匹配,在输入列表用完时停止
rotations xs = map (takeSame xs) $ takeSame xs (tails (xs ++ xs)) where
takeSame [] _ = []
takeSame (_:xs) (y:ys) = y:takeSame xs ys
此外,由于它使用了尾部,并且仅使用单一浓缩,因此它在记忆方面的表现比其他一些选择要好得多。当然,它也可以正确地处理无限列表
rotations (x:xs) = (xs++[x]):(rotations (xs++[x]) )
这将创建连续的惰性旋转
现在只需要第一个唯一的,它将等于原始列表的长度
take (length xs) (rotations xs)
从头开始:
data[]a=[]a:[a]
结束::a->[a]->[a]
结束y[]=y:[]
结束y(x:xs)=x:y`end`xs
--这样`end x xs=xs++[x]`
旋转::[a]->[[a]]
旋转[]=[]
旋转X=旋转X
哪里
rots xs@(x:xs')=xs:rots(x`end`xs'))
--无限旋转列表
旋转::[a]->[[a]]
旋转X=旋转X(旋转X)
哪里
rots[].[]
rots(uuxs)(r:rs)=r:rots xs-rs
--所有唯一旋转,`forall xs.`相当于`take
--(长度xs)(旋转xs)`
或:
{-#语言模式}
旋转::[a]->[a]
旋转[]=[]
旋转(x:xs)=x`end`xs
哪里
结束y[]=y:[]
结束y(x:xs)=x:end y xs
迭代::Int->(a->a)->a->[a]
迭代0=[]
迭代n f x=的情况f x
x'->x':迭代(n-1)fx'
长度::[a]->Int
长度xs=长度0 xs
哪里
伦恩!n[]=n
伦恩!n(n:xs)=len(n+1)xs
旋转::[a]->[[a]]
旋转X
take (length xs) (rotations xs)
rotations :: [a] -> [[a]]
rotations xs = take (length xs) (iterate (\(y:ys) -> ys ++ [y]) xs)
import Data.List
rotate x=[take (length x) $ drop i $ cycle x | i<-[0..length x-1]]