Reputation: 1
This is my function:
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)))
I want my function to repeat itself for n-times. I guess you do this with guardians or the "while" operator, but I don't know quite how. The goal is to rotate elements of a integer-list n-times.
This was my failed approach:
rota :: Int -> [Int] -> [Int]
rota n (x:xs) = rotatebyone (x:xs)
where
rota n
| n > 1 = rota (n - 1)
| otherwise = rotatebyone (x:xs)
Upvotes: 0
Views: 131
Reputation: 477607
Let us first clean up the rotatebyone
function:
rotatebyone :: [Int] -> [Int]
rotatebyone [] = []
rotatebyone (x:xs) = ((tail (x:xs)) ++ (take 1 (x:xs)))
Here you call tail
on (x:xs)
but that is quite weird, since we already have a tail: xs
. So we can replace tail (x:xs)
with xs
.
The same holds for take 1 (x:xs)
. This will construct a list with the head of the list, so [x]
. So we can clean up the code to:
rotatebyone :: [Int] -> [Int]
rotatebyone [] = []
rotatebyone (x:xs) = xs ++ [x]
Furthermore your function only works on lists of type Int
(it has signature [Int] -> [Int]
). We can however rotate lists by one regardless the type of elements these lists hold, so we can write it as:
rotatebyone :: [a] -> [a]
rotatebyone [] = []
rotatebyone (x:xs) = xs ++ [x]
Now we can look for a way to rotate n
times with:
rotatebyn :: Int ->[a] -> [a]
rotatebyn ...
In case we wish to rotate over zero places, then the list remains the same, so as a base case we can write:
rotatebyn 0 l = l
and in case we want to rotate over one or more places, we can rotate over one place by the rotatebyone
function, and then rotate this result another n-1
times:
rotatebyn n l = rotatebyn (n-1) (rotateone n)
or these together:
rotatebyn :: Int ->[a] -> [a]
rotatebyn 0 l = l
rotatebyn n l = rotatebyn (n-1) (rotateone n)
But the above is, like @dfeuer says not efficient: it takes O(n) time to rotate a list, if we need to do this k times, the time complexity is thus O(n k).
Note furthermore that the above function only works in case n
is greater than or equal to zero.
Upvotes: 0
Reputation: 48621
You shouldn't rotate multiple places by rotating once and doing that repeatedly. Let's look at a slightly cleaner version of your single rotation to see why:
rotateLeft1 :: [a] -> [a]
rotateLeft1 [] = []
rotateLeft1 (x:xs) = xs ++ [x]
The problem is that p ++ q
takes time linear in the length of p
. You're doing that once for each rotation step, which wastes a lot of time. Let's look at it differently, with an example.
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]
There's one tricky bit left: what if I rotate a list by a number greater than its length? I'll let you give that a go yourself.
Upvotes: 1