Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a rotates function

Tags:

haskell

How to define a rotates function that generates all rotations of the given list?

For example: rotates [1,2,3,4] =[[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3]]

I wrote a shift function that can rearrange the order

 shift ::[Int]->[Int]

 shift x=tail ++ take 1 x

but I don't how to generate these new arrays and append them together.

like image 876
Byron0324 Avatar asked Oct 03 '11 06:10

Byron0324


4 Answers

Another way to calculate all rotations of a list is to use the predefined functions tails and inits. The function tails yields a list of all final segments of a list while inits yields a list of all initial segments. For example,

tails [1,2,3] = [[1,2,3], [2,3], [3],   []]

inits [1,2,3] = [[],      [1],   [1,2], [1,2,3]]

That is, if we concatenate these lists pointwise as indicated by the indentation we get all rotations. We only get the original list twice, namely, once by appending the empty initial segment at the end of original list and once by appending the empty final segment to the front of the original list. Therefore, we use the function init to drop the last element of the result of applying zipWith to the tails and inits of a list. The function zipWith applies its first argument pointwise to the provided lists.

allRotations :: [a] -> [[a]]
allRotations l = init (zipWith (++) (tails l) (inits l))

This solution has an advantage over the other solutions as it does not use length. The function length is quite strict in the sense that it does not yield a result before it has evaluated the list structure of its argument completely. For example, if we evaluate the application

allRotations [1..]

that is, we calculate all rotations of the infinite list of natural numbers, ghci happily starts printing the infinite list as first result. In contrast, an implementation that is based on length like suggested here does not terminate as it calculates the length of the infinite list.

like image 178
Jan Christiansen Avatar answered Nov 19 '22 03:11

Jan Christiansen


shift (x:xs)  =  xs ++ [x]
rotates xs    =  take (length xs) $ iterate shift xs

iterate f x returns the stream ("infinite list") [x, f x, f (f x), ...]. There are n rotations of an n-element list, so we take the first n of them.

like image 28
Fred Foo Avatar answered Nov 19 '22 02:11

Fred Foo


The following

shift :: [a] -> Int -> [a]
shift l n = drop n l  ++ take n l

allRotations :: [a] -> [[a]]
allRotations l = [ shift l i | i <- [0 .. (length l) -1]]

yields

> ghci
Prelude> :l test.hs
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> allRotations [1,2,3,4]
[[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3]]

which is as you expect.

I think this is fairly readable, although not particularly efficient (no memoisation of previous shifts occurs).


If you care about efficiency, then

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

allRotations :: [a] -> [[a]]
allRotations l = take (length l) (iterate shift l)

will allow you to reuse the results of previous shifts, and avoid recomputing them.

Note that iterate returns an infinite list, and due to lazy evaluation, we only ever evaluate it up to length l into the list.


Note that in the first part, I've extended your shift function to ask how much to shift, and I've then a list comprehension for allRotations.

like image 4
MGwynne Avatar answered Nov 19 '22 03:11

MGwynne


The answers given so far work fine for finite lists, but will eventually error out when given an infinite list. (They all call length on the list.)

shift :: [a] -> [a]
shift xs = drop 1 xs ++ take 1 xs

rotations :: [a] -> [[a]]
rotations xs = zipWith const (iterate shift xs) xs

My solution uses zipWith const instead. zipWith const foos bars might appear at first glance to be identical to foos (recall that const x y = x). But the list returned from zipWith terminates when either of the input lists terminates.

So when xs is finite, the returned list is the same length as xs, as we want; and when xs is infinite, the returned list will not be truncated, so will be infinite, again as we want.

(In your particular application it may not make sense to try to rotate an infinite list. On the other hand, it might. I submit this answer for completeness only.)

like image 1
dave4420 Avatar answered Nov 19 '22 03:11

dave4420