如何使我的简单Haskell函数递归?

时间:2017-11-13 02:43:36

标签: haskell

这是我的功能:

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)

2 个答案:

答案 0 :(得分:1)

您不应通过旋转一次并重复执行此操作来旋转多个位置。让我们看看你的单轮旋转的稍微清洁版本,看看为什么:

rotateLeft1 :: [a] -> [a]
rotateLeft1 [] = []
rotateLeft1 (x:xs) = xs ++ [x]

问题是p ++ qp的长度上需要时间线性。你为每个旋转步骤做了一次,这浪费了很多时间。让我们用一个例子来区别对待它。

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大于或等于零的情况下有效。