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

后端 未结 11 1504
盖世英雄少女心
盖世英雄少女心 2020-12-19 03:40

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

相关标签:
11条回答
  • 2020-12-19 03:55

    Is it like below:

    scala> def rotatedView(i:Int)=Seq(1,2,3,4,5).drop(i)++Seq(1,2,3,4,5).take(i)
    rotatedView: (i: Int)Seq[Int]
    
    scala> rotatedView(1)
    res48: Seq[Int] = List(2, 3, 4, 5, 1)
    
    scala> rotatedView(2)
    res49: Seq[Int] = List(3, 4, 5, 1, 2)
    
    0 讨论(0)
  • 2020-12-19 03:57

    Another one line solution if you don't need to validate the "offset":

       def rotate[T](seq: Seq[T], offset: Int): Seq[T] = Seq(seq, seq).flatten.slice(offset, offset + seq.size)

    0 讨论(0)
  • 2020-12-19 04:00

    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.

    0 讨论(0)
  • 2020-12-19 04:07

    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)
    
    0 讨论(0)
  • 2020-12-19 04:08

    We can simply use foldLeft to reverse a list as below.

      val input = List(1,2,3,4,5)
    
      val res = input.foldLeft(List[Int]())((s, a) => { List(a) ++: s})
    
      println(res) // List(5, 4, 3, 2, 1)
    
    0 讨论(0)
  • 2020-12-19 04:10

    Following the OP's comment that they want to fold over it, here's a slightly different take on it that avoids calculating the length of the sequence first.

    Define an iterator that will iterate over the rotated sequence

    class RotatedIterator[A](seq: Seq[A], start: Int) extends Iterator[A] {
      var (before, after) = seq.splitAt(start)
      def next = after match {
        case Seq()  =>
          val (h :: t) = before; before = t; h
        case h :: t => after = t; h
      }
      def hasNext = after.nonEmpty || before.nonEmpty
    }
    

    And use it like this:

    val seq = List(1, 2, 3, 4, 5)  
    val xs = new RotatedIterator(seq, 2)
    println(xs.toList)         //> List(3, 4, 5, 1, 2)
    
    0 讨论(0)
提交回复
热议问题