6

I have a function to rotate a list:

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

Now I want a function that gives a list with every possible rotation of a finite list:

rotateAll :: [a] -> [[a]]

In a imperative language, I would do something like (in pseudocode)

for i = 1 to length of list
  append list to rotateList
  list = rotate(list)

Of course, thinking imperatively probably doesn't help me find a functional solution to this problem. I'm looking for some hints as to how to tackle this.

Additional thoughts:

To solve this, I have two issues to tackle. First I need to repeatedly rotate a list and collect each result into a list. So a first solution needs to do something like

rotateAll xs = [xs (rotate xs) (rotate (rotate xs)) (rotate (rotate (rotate xs))) ...]

Of course I don't know how many times to do this. I'd be satisfied to do this infinitely and then use take (length xs) to get the finite number of lists I desire. This actually demonstrates the second issue: determining when to stop. I don't know if using take is the most efficient or elegant way to solve the problem, but it came to mind as I was typing this and should work.

Addendum: Now that I have found two solutions on my own or with hints. I will gladly welcome any other solutions that are faster or use different approaches. Thanks!

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
  • 2
    Take a look at the `iterate` function. – Tilo Wiklund Aug 08 '12 at 18:18
  • Be aware that you will be traversing the list once for each iteration (append is O(n), for n the length of the list) and once for calculating the length of `xs` (which is also O(n)) – Tilo Wiklund Aug 08 '12 at 18:28
  • It might be interesting to note that you can solve this for infinite input lists, too - so long as you rotate the correct direction. Coming up with a solution that works well for both infinite and finite inputs is a good further exercise. – Carl Aug 08 '12 at 18:30
  • @TiloWiklund I usually want to first make a solution that works and tune for efficiency later. It's still good to keep these concerns in mind during an initial solution. So thanks for pointing it out! I'll revisit my `rotate` function if that becomes a concern later. – Code-Apprentice Aug 08 '12 at 18:35
  • @Carl Hmmm...that's some food for thought. It seems that I would have to rotate an infinite list in the same direction as I am a finite list by taking the head off. However, I can't exactly append the head to the end of an infinite list...hmmm – Code-Apprentice Aug 08 '12 at 18:36
  • @Code-Guru: You can append to the end of an infinite list, but semantically those two lists will behave exactly the same. That is, `ℕ ++ [1] ~ ℕ`. – Vitus Aug 08 '12 at 18:44
  • @Vitus So `xs ++ ys` where `xs` is some infinite list won't hang in an infinite loop? – Code-Apprentice Aug 08 '12 at 18:56
  • @Carl for infinite lists, rotations make little sense, as you cannot append to the end; see `tails` in `Data.List`. – sdcvvc Aug 08 '12 at 19:05
  • @Code-Guru `xs ++ ys` is the same as `xs` when `xs` is infinite. – sdcvvc Aug 08 '12 at 19:05
  • @sdcvvc So lazy evaluation means that nothing is actually executed to do this "append"? – Code-Apprentice Aug 08 '12 at 19:07
  • @Code-Guru append is defined as `(x:xs) ++ ys = x:(xs ++ ys); [] ++ ys = ys`, when `xs` is infinite the second line will never fire. – sdcvvc Aug 08 '12 at 19:12
  • @sdcvvc So it's pattern matching along with lazy evaluation. Gotchya. – Code-Apprentice Aug 08 '12 at 19:15
  • Here's a cute hack to avoid calling `length`: `zipWith const (iterate rotate xs) xs`. – Daniel Wagner Aug 08 '12 at 20:19
  • @DanielWagner That's interesting. I don't think it buys any efficiency gains, though ;-( – Code-Apprentice Aug 08 '12 at 20:23
  • @Code-Guru Actually, it's quite important: this allows your function to be fully lazy (whereas `length` is strict). Try the version with `length` and the version with `zipWith` on an infinite list in ghci, and compare what happens. – Daniel Wagner Aug 08 '12 at 20:26
  • @DanielWagner I'm all about laziness! – Code-Apprentice Aug 08 '12 at 20:28
  • @NormanRamsey yup, I realized that when Jan posted his answer. In fact, I flagged this thread myself. *lol* – Code-Apprentice Aug 09 '12 at 00:18

11 Answers11

7

Use the predefined functions in Data.List! You can get a list of all rotations using four function calls, no recursion, and no rotate function.

You asked not to have a full solution posted here. For those who want to see it, a full solution (one line of code) appears at http://pastebin.com/atGiw1ig.

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
3

Aside from iterate, you could write a function that generates a list of n rotations. Case n=0 would just wrap the input in a list and case n=m+1 would append the input to the result of case m. Although using standard functions is generally preferred, sometimes writing your own solutions is healthy.

Karolis Juodelė
  • 3,708
  • 1
  • 19
  • 32
  • Strangely, I basically used that same idea as I thought about this after I posted my question. I didn't know about the `iterate` function, so that will be added to my Haskell aresenal. – Code-Apprentice Aug 08 '12 at 18:31
3

Here is a version that is fully lazy in both the list of rotations itself and each individual entry in the list. The key is is that rather than pre-compute the length, just match up the elements in your result to the elements in the list as you go, stopping when your input list runs out.

rotations xs = map (takeSame xs) $ takeSame xs (tails (xs ++ xs)) where
    takeSame [] _ = [] 
    takeSame (_:xs) (y:ys) = y:takeSame xs ys

In addition this is much better behaved memory-wise than some of the other choices due to its use of tails and only the single concatination. Of course, it also handles infinite lists properly as well.

John Meacham
  • 785
  • 7
  • 9
2

You might also want to consider this site How to define a rotates function that answers the same question.

Edit due to Comment: The implementations based on rotate as well as the one based on inits and tails should be quadratic in the length of the list. However, the one based on inits and tails should be less efficient because it performs several quadratic traversals. Though note that these statements only hold if you evaluate the result completely. Furthermore, the compiler might be able to improve the code so you have to treat these statements with caution.

Community
  • 1
  • 1
Jan Christiansen
  • 3,153
  • 1
  • 19
  • 16
2
rotations (x:xs) = (xs++[x]):(rotations (xs++[x]) ) 

this creates continues lazy rotations now just take the first unique ones, which will be equal to the length of the original list

take (length xs) (rotations xs)
renjith
  • 551
  • 1
  • 5
  • 7
1

I came up with two solutions. First is a hand-crafted one that came to me after I posted my question:

rotateAll :: [a] -> [[a]]
rotateAll xs = rotateAll' xs (length xs)
    where rotateAll' xs 1 = [xs]
          rotateAll' xs n = xs : rotateAll' (rotate xs) (n - 1)

The other uses @Tilo's suggestion:

rotateAll :: [a] -> [[a]]
rotateAll xs = take (length xs) (iterate rotate xs)
Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
1

You could also generate them recursively. Generating all rotations of an empty or single element list is trivial, and generating the rotations of x:xs is a matter of inserting x into the correct position of all the rotations of xs.

You could do this by generating the indexes to insert into (simply the list [1, 2, ...] assuming the previous rotations are in this order) and use zipWith to insert x into the correct positions.

Alternatively you can split the rotations around the position using a combination of inits and tails and use zipWith to glue them back together.

Tilo Wiklund
  • 751
  • 1
  • 10
  • 15
1

Addendum: Now that I have found two solutions on my own or with hints. I will gladly welcome any other solutions that are faster or use different approaches. Thanks!

Since no else pointed out using cycle I thought I would add this solution for finite lists:

rotations x = let n = length x in map (take n) $ take n (tails (cycle x))

For an infinite list x the rotations are just tails x.

Evaluating the cycle x is O(n) time and space, each element of tails is O(1), and take n is O(n) time and space but the two take n are nested so evaluating the whole result is O(n^2) time and space.

If it garbage collects each rotation before lazily generating the next one then the space is theoretically O(n).

If you are clever about how much you are consuming then you do not need map (take n) and can just walk the cycle x or take n (tails (cycle x)) and keep the space O(n).

Chris Kuklewicz
  • 8,123
  • 22
  • 33
1

From scratch:

data [] a = [] | a : [a]

end :: a -> [a] -> [a]
end y []       = y : []
end y (x : xs) = x : y `end` xs

-- such that `end x xs  =  xs ++ [x]`

rotating :: [a] -> [[a]]
rotating [] = []
rotating xs = rots xs
   where
      rots xs@(x : xs') = xs : rots (x `end` xs')

-- An infinite list of rotations

rotations :: [a] -> [[a]]
rotations xs = rots xs (rotating xs)
   where
      rots []       _        = []
      rots (_ : xs) (r : rs) = r : rots xs rs

-- All unique rotations, `forall xs.` equivalent to `take
-- (length xs) (rotating xs)`

Or:

{-# LANGUAGE BangPatters #-}

rotate :: [a] -> [a]
rotate []       = []
rotate (x : xs) = x `end` xs
   where
      end y []       = y : []
      end y (x : xs) = x : end y xs

iterate :: Int -> (a -> a) -> a -> [a]
iterate 0 _ _ = []
iterate n f x = case f x of
                   x' -> x' : iterate (n - 1) f x'

length :: [a] -> Int
length xs = len 0 xs
   where
      len !n []       = n
      len !n (_ : xs) = len (n + 1) xs

rotations :: [a] -> [[a]]
rotations xs = iterate (length xs) rotate xs

-- Starting with single rotation

Or, integrated:

rotations :: [a] -> [[a]]
rotations [] = [[]]
rotations xs = rots xs xs
   where
      rots []       _            = []
      rots (_ : xc) xs@(x : xs') = xs : rots xc (x `end` xs')

      end y []       = y : []
      end y (x : xs) = x : end y xs
1

Maybe you can use Data.List

import Data.List
rotate x=[take (length x) $ drop i $ cycle x | i<-[0..length x-1]]
Aleph0
  • 5,816
  • 4
  • 29
  • 80
0

I used the following rotations function as a helper for my permutations algorithm. It seems to be the fastest among all here.

rotations :: [a] -> [[a]]
rotations xs = take (length xs) (iterate (\(y:ys) -> ys ++ [y]) xs)
Community
  • 1
  • 1
Redu
  • 25,060
  • 6
  • 56
  • 76
  • How does this differ from my own solution? http://stackoverflow.com/a/11870855/1440565 – Code-Apprentice Apr 20 '17 at 14:01
  • @Code-Apprentice If you mean your second snippet with the `rotate` function yes it should be basically the same if the rotate function that you refer is in fact `\(y:ys) -> ys ++ [y]`. This (or these) seem to be the best way so far. – Redu Apr 20 '17 at 14:11