Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you rotate (circular shift) of a Scala collection

I can do this quite easily, and cleanly, using a for loop. For instance, if I wanted to traverse a Seq from every element back to itself I would do the following:

val seq = Seq(1,2,3,4,5)

for (i <- seq.indices) {
    for (j <- seq.indices) {
        print(seq(i + j % seq.length))
    }
}

But as I'm looking to fold over the collection, I'm wondering if there is a more idiomatic approach. A recursive approach would allow me to avoid any vars. But basically, I'm wondering if something like the following is possible:

seq.rotatedView(i)

Which would create a rotated view, like rotating bits (or circular shift).

like image 361
Nadir Muzaffar Avatar asked Sep 21 '15 23:09

Nadir Muzaffar


2 Answers

A simple method is to concatenate the sequence with itself and then take the slice that is required:

(seq ++ seq).slice(start, start + seq.length)

This is just a variant of the drop/take version but perhaps a little clearer.

like image 129
Tim Avatar answered Nov 04 '22 17:11

Tim


Another tail-recursive approach. When I benchmarked it with JMH it was about 2 times faster than solution based on drop/take:

def rotate[A](list: List[A], by: Int): List[A] = {

    @tailrec
    def go(list: List[A], n: Int, acc: List[A]): List[A] = {

      if(n > 0) {
        list match {
          case x :: xs => go(xs, n-1, x :: acc)
        }
      } else {
        list ++ acc.reverse
      }

    }

    if (by < 0) {
      go(list, -by % list.length, Nil)
    } else {
      go(list, list.length - by % list.length, Nil)
    }    
}

//rotate right
rotate(List(1,2,3,4,5,6,7,8,9,10), 3) // List(8, 9, 10, 1, 2, 3, 4, 5, 6, 7) 

//use negative number to rotate left
rotate(List(1,2,3,4,5,6,7,8,9,10), -3) // List(4, 5, 6, 7, 8, 9, 10, 1, 2, 3)
like image 20
Krzysztof Atłasik Avatar answered Nov 04 '22 16:11

Krzysztof Atłasik