2015-06-27 2 views
1

Я пытаюсь свернуть список, например так:Встроенный метод прокатки списка Scala или Seq

val notRolled = (1 to 5).toList  // List(1,2,3,4,5) 

val rolledBy1 = rollList(notRolled,1) // List(2,3,4,5,1) 
val rolledBy2 = rollList(notRolled,2) // List(3,4,5,1,2) 
val rolledBy3 = rollList(notRolled,3) // List(4,5,1,2,3) 
val rolledBy4 = rollList(notRolled,4) // List(5,1,2,3,4) 
val rolledBy5 = rollList(notRolled,5) // List(1,2,3,4,5) 

val rolledByM1 = rollList(notRolled,-1) // List(5,1,2,3,4) 
val rolledByM2 = rollList(notRolled,-2) // List(4,5,1,2,3) 
val rolledByM3 = rollList(notRolled,-3) // List(3,4,5,1,2) 
val rolledByM4 = rollList(notRolled,-4) // List(2,3,4,5,1) 
val rolledByM5 = rollList(notRolled,-5) // List(1,2,3,4,5) 

Я посмотрел на scala-lang.org и не могу найти ничего, что соответствует мое требование.

Есть ли встроенный метод для прокатки списка?

Если нет, то есть более эффективный способ сделать это, чем это:

def rollList[T](theList: List[T], itemsToRoll: Int): List[T] = { 
    val split = { 
    if (itemsToRoll < 0) (theList.size + itemsToRoll % theList.size) 
    else itemsToRoll % theList.size 
    } 
    val (beginning, end) = theList.splitAt(split) 
    end ::: beginning 
} 
+0

Этот вопрос является продублируйте из http://stackoverflow.com/questions/8876769/best -за-практика сдвиг-а-последовательность-в-круговой-образом? –

ответ

5

Использование Пополните-моя библиотека шаблон позволит вам добавить метод roll непосредственно ко всем коллекциям, так что вы можете звоните myList.roll(2), в отличие от roll(myList, 1).

Используя общий CanBuildFrom шаблон позволяет сделать roll возвращение лучший возможный тип этих коллекций (так что roll на List возвратит List, но roll на Iterable возвратит Iterable).

Все вместе, вот один вариант, который придерживается этих моделей:

import scala.collection.generic.CanBuildFrom 
import scala.collection.TraversableLike 

implicit class TraversableWithRoll[A, Repr <: Traversable[A]](val xs: TraversableLike[A, Repr]) extends AnyVal { 
    def roll[That](by: Int)(implicit bf: CanBuildFrom[Repr, A, That]): That = { 
    val builder = bf() 
    val size = xs.size 
    builder.sizeHint(xs) 
    val leftBy = if (size == 0) 0 else ((by % size) + size) % size 
    builder ++= xs.drop(leftBy) 
    builder ++= xs.take(leftBy) 
    builder.result 
    } 
} 

Это позволяет сделать некоторые из следующих:

List(1, 2, 3, 4, 5).roll(2) //List(4,5,1,2,3) 
Seq(3, 4, 5).roll(-1) //Seq(5, 3, 4) 

Обратите внимание на преимущества текучего синтаксиса обеспечиваемых шаблон обогащения-my-library и более сильные типы, которые допускаются с помощью использования неявного значения CanBuildFrom.

+0

Красиво сделано! Однако есть одна проблема в том, что он генерирует исключение, если вы пытаетесь свернуть пустую коллекцию (size == 0). Также мне интересно, нельзя ли упростить вычисление 'leftBy'. Это то, что я придумал (возможно, лучше): 'leftBy = ((по размеру) + размер)% size' – jwvh

+0

@jwvh Хорошие уловы, я включил их в ответ. –

0

Вот один из способов имитации циклического списка в функциональном стиле. Я сделал так, чтобы положительный i выполнил правый сдвиг и отрицательный сдвиг влево. Это противоположность тому, что имеет этот вопрос, но я чувствовал, что это более естественно.

def shift[T](c: List[T], i: Int) = { 
    if(c.isEmpty) c 
    else { 
     val (h,t) = if (i >= 0) c.splitAt(c.size - i%c.size) 
      else c.splitAt(- i%c.size) 
     t ++ h 
    } 
} 

Вот некоторые примеры в РЕПЛ:

cala> shift(List(1,2,4), 1) 
res: List[Int] = List(4, 1, 2) 

scala> shift(List(1,2,4), 2) 
res: List[Int] = List(2, 4, 1) 

scala> shift(List(1,2,4), 3) 
res: List[Int] = List(1, 2, 4) 

scala> shift(List(1,2,4), 4) 
res: List[Int] = List(4, 1, 2) 

scala> shift(List(1,2,4), 5) 
res: List[Int] = List(2, 4, 1) 

scala> shift(List(1,2,4), 6) 
res: List[Int] = List(1, 2, 4) 

scala> shift(List(1,2,4), -1) 
res60: List[Int] = List(2, 4, 1) 

scala> shift(List(1,2,4), -2) 
res: List[Int] = List(4, 1, 2) 

scala> shift(List(1,2,4), -3) 
res: List[Int] = List(1, 2, 4) 

scala> shift(List(1,2,4), -4) 
res: List[Int] = List(2, 4, 1) 

scala> shift(List(1,2,4), -5) 
res: List[Int] = List(4, 1, 2) 

scala> shift(List(1,2,4), -6) 
res: List[Int] = List(1, 2, 4) 
+0

Сбой для пустого списка. –

+0

@Shyamendra Solanki такая же, как версия OP, поэтому, очевидно, он не предназначен для запуска в пустых списках или вы тоже хотите задать вопрос «-1» OP? – dk14

+0

Хорошая добыча! просто добавил предложение, чтобы поддержать это. – marios

0
def rotate[A] (list: List[A], by: Int) = { 
    if (list.isEmpty) list 
    else { 
     val shift = (by % list.size + list.size) % list.size 
     val (l, r) = list.splitAt(shift) 
     r ++ l 
    } 
} 

val list = List(1, 2, 3, 4, 5) 
rotate(list, 1) // List(2, 3, 4, 5, 1) 
rotate(list, 2) // List(3, 4, 5, 1, 2) 
rotate(list, -1) // List(5, 1, 2, 3, 4) 
rotate(list, -2) // List(4, 5, 1, 2, 3) 

rotate(list, -9) // List(2, 3, 4, 5, 1) 
0

Это мой чистый функционал очень простое предложение:

def circ[A](L: List[A], times: Int): List[A] = { 

    if (times == 0 || L.size < 2) L 
    else circ(L.drop(1) :+ L.head , times-1) 
} 

val G = List("1a","2b","3c") 

println(circ(G,1)) //List(2b, 3c, 1a) 
println(circ(G,2)) //List(3c, 1a, 2b) 
println(circ(G,3)) //List(1a, 2b, 3c) 
println(circ(G,4)) //List(2b, 3c, 1a) 

Добавленная раз < 0 (скатывание назад):

def circ[A](L: List[A], times: Int): List[A] = { 
    if (times == 0 || L.size < 2) L 
    else if (times < 0) circ(L.reverse,-times).reverse 
    else     circ(L.drop(1) :+ L.head , times-1) 
} 
+0

добавлено обратное направление – francisco

0

Дано:

val notRolled = (1 to 5).toList 

Допустим, мы хотим поворот со сдвигом = 3

val shift = 3 

notRolled.zipWithIndex.groupBy(_._2 < shift).values.flatten.map(_._1) 

Для отрицательных значений следует добавить 5 (списки размера), так что для сдвига = -3 рассчитает

val shift = -3 
notRolled.zipWithIndex.groupBy(_._2 < 2).values.flatten.map(_._1) 
0

Я собираюсь включить идеи выше, о CanBuildFrom, как только Я понимаю Scala лучше (!), Но тем временем я должен был сделать именно это ради написания алгоритма преобразования Burrows-Wheeler. Здесь, без take() или drop(), и без каких-либо ошибок на пустые коллекции (и, да, бок осуществления кода):

def roll[A](xs: Traversable[A]): Iterator[Traversable[A]] = { 
    var front = xs 
    var back = collection.mutable.Buffer[A]() 

    Iterator.continually(front ++ back).takeWhile { _ => 
    val done = front.isEmpty 
    if (!done) { 
     back :+= front.head 
    front = front.tail 
    } 
    !done 
    } 
} 
Смежные вопросы