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

后端 未结 11 1731
盖世英雄少女心
盖世英雄少女心 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 04:19

    This ought to do it in a fairly generic way, and allow for arbitrary rotations:

    def rotateLeft[A](seq: Seq[A], i: Int): Seq[A] = {
        val size = seq.size
        seq.drop(i % size) ++ seq.take(i % size)
    }
    
    def rotateRight[A](seq: Seq[A], i: Int): Seq[A] = {
        val size = seq.size
        seq.drop(size - (i % size)) ++ seq.take(size - (i % size))
    }
    

    The idea is simple enough, to rotate left, drop the first i elements from the left, and take them again from the left to concatenate them in the opposite order. If you don't mind calculating the size of the collection, you can do your operations modulo the size, to allow i to be arbitrary.

    scala> rotateRight(seq, 1)
    res34: Seq[Int] = List(5, 1, 2, 3, 4)
    
    scala> rotateRight(seq, 7)
    res35: Seq[Int] = List(4, 5, 1, 2, 3)
    
    scala> rotateRight(seq, 70)
    res36: Seq[Int] = List(1, 2, 3, 4, 5)
    

    Similarly, you can use splitAt:

    def rotateLeft[A](seq: Seq[A], i: Int): Seq[A] = {
        val size = seq.size
        val (first, last) = seq.splitAt(i % size)
        last ++ first
    }
    
    def rotateRight[A](seq: Seq[A], i: Int): Seq[A] = {
        val size = seq.size
        val (first, last) = seq.splitAt(size - (i % size))
        last ++ first
    }
    

    To make it even more generic, using the enrich my library pattern:

    import scala.collection.TraversableLike
    import scala.collection.generic.CanBuildFrom
    
    implicit class TraversableExt[A, Repr <: TraversableLike[A, Repr]](xs: TraversableLike[A, Repr]) {
    
        def rotateLeft(i: Int)(implicit cbf: CanBuildFrom[Repr, A, Repr]): Repr = {
            val size = xs.size
            val (first, last) = xs.splitAt(i % size)
            last ++ first
        }
    
        def rotateRight(i: Int)(implicit cbf: CanBuildFrom[Repr, A, Repr]): Repr = {
            val size = xs.size
            val (first, last) = xs.splitAt(size - (i % size))
            last ++ first
        }
    
    }
    
    scala> Seq(1, 2, 3, 4, 5).rotateRight(2)
    res0: Seq[Int] = List(4, 5, 1, 2, 3)
    
    scala> List(1, 2, 3, 4, 5).rotateLeft(2)
    res1: List[Int] = List(3, 4, 5, 1, 2)
    
    scala> Stream(1, 2, 3, 4, 5).rotateRight(1)
    res2: scala.collection.immutable.Stream[Int] = Stream(5, ?)
    

    Keep in mind these are not all necessarily the most tuned for performance, and they also can't work with infinite collections (none can).

提交回复
热议问题