这是我的功能:
rotatebyone :: [Int] -> [Int]
rotatebyone [] = []
rotatebyone (x:xs) = ((tail (x:xs)) ++ (take 1 (x:xs)))
rotatebyn :: Int -> [Int] -> [Int]
rotatebyn n [] = []
rotatebyn 1 (x:xs) = rotatebyone (x:xs)
rotatebyn 2 (x:xs) = (rotatebyone (rotatebyone (x:xs)))
我希望我的功能能够重复n次。我猜你是用监护人或“while”操作员做的,但我不知道怎么做。目标是将整数列表的元素旋转n次。
这是我失败的做法:
rota :: Int -> [Int] -> [Int]
rota n (x:xs) = rotatebyone (x:xs)
where
rota n
| n > 1 = rota (n - 1)
| otherwise = rotatebyone (x:xs)
答案 0 :(得分:1)
您不应通过旋转一次并重复执行此操作来旋转多个位置。让我们看看你的单轮旋转的稍微清洁版本,看看为什么:
rotateLeft1 :: [a] -> [a]
rotateLeft1 [] = []
rotateLeft1 (x:xs) = xs ++ [x]
问题是p ++ q
在p
的长度上需要时间线性。你为每个旋转步骤做了一次,这浪费了很多时间。让我们用一个例子来区别对待它。
rotateLeft 3 [1,2,3,4,5,6,7,8]
-- split the list
([1,2,3], [4,5,6,7,8])
-- swap the pieces
([4,5,6,7,8], [1,2,3])
-- Append
[4,5,6,7,8,1,2,3]
还有一个棘手的问题:如果我按照大于其长度的数字旋转列表怎么办?我会让你自己去做。
答案 1 :(得分:0)
让我们先清理rotatebyone
函数:
rotatebyone :: [Int] -> [Int]
rotatebyone [] = []
rotatebyone (x:xs) = ((tail (x:xs)) ++ (take 1 (x:xs)))
您可以在tail
上致电(x:xs)
,但这很奇怪,因为我们已经有一个尾巴:xs
。因此,我们可以将tail (x:xs)
替换为xs
。
同样适用于take 1 (x:xs)
。这将构建一个包含列表头部的列表,因此[x]
。所以我们可以将代码清理为:
rotatebyone :: [Int] -> [Int]
rotatebyone [] = []
rotatebyone (x:xs) = xs ++ [x]
此外,您的函数仅适用于Int
类型的列表(它具有签名[Int] -> [Int]
)。但是,无论这些列表包含哪些元素,我们都可以将列表旋转一个,因此我们可以将其写为:
rotatebyone :: [a] -> [a]
rotatebyone [] = []
rotatebyone (x:xs) = xs ++ [x]
现在,我们可以通过以下方式寻找旋转n
次的方法:
rotatebyn :: Int ->[a] -> [a]
rotatebyn ...
如果我们希望旋转零位,那么列表保持不变,所以我们可以写一个基本情况:
rotatebyn 0 l = l
如果我们想要旋转一个或多个地方,我们可以通过rotatebyone
函数旋转一个地方,然后再将此结果旋转n-1
次:
rotatebyn n l = rotatebyn (n-1) (rotateone n)
或者这些:
rotatebyn :: Int ->[a] -> [a]
rotatebyn 0 l = l
rotatebyn n l = rotatebyn (n-1) (rotateone n)
但是上面的内容就像@dfeuer说的那样效率不高:如果我们需要 k O(n)时间>次,时间复杂度因此 O(nk)。
此外请注意,上述功能仅在n
大于或等于零的情况下有效。