views:

186

answers:

4

I need iterate through a List but circular way. I need too add new elements to the list and iterate over all elements (olds and news elements), How I do it? Is there any data structure for them?

+8  A: 

One option is to use the Stream class to create a lazy, circular, infinite sequence:

scala> val values = List(1, 2, 3)
values: List[Int] = List(1, 2, 3)

scala> Stream.continually(values.toStream).flatten.take(9).toList
res2: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3)

or this way:

val values = List(1, 2, 3)

def circularStream(values: List[Int],
                   remaining: List[Int] = List()): Stream[Int] = {

  if (remaining.isEmpty)
    circularStream(values,values)
  else
    Stream.cons(remaining.head, circularStream(values, remaining.drop(1)))
}

circularStream(values).take(9).toList //Same result as example #1
dbyrne
As a side note, in clojure you can do this quite succinctly: `(take 9 (cycle '(1 2 3)))`
dbyrne
+4  A: 

This sort of thing really deserves to be in standard stream library, but doesn't appear to be. dbryne's answer with a stream works well, or if you prefer it in for-comprehension form

val listToRepeat:List[Foo]
val forever:Stream[Foo] = for(x<-Stream.continually(1); y<-listToRepeat) yield y

The first stream generator keeps things going forever even though you are ignoring the value. The second generator gets implicitly flattened into the infinite stream you want.

Dave Griffith
You don’t need an argument for `Stream.continually()`, i think using a `Stream[Unit]` looks a little less confusing than a `Stream[Int]`.
Debilski
+6  A: 
def forever:Stream[Int] = Stream(1,2,3) append forever
Landei
+4  A: 

I think maybe this is what you want; the ability to add new elements to your list even as you are iterating it. The code is ugly but it seems to work.

import scala.collection.mutable.Queue

class Circular[A](list: Seq[A]) extends Iterator[A]{

  val elements = new Queue[A] ++= list
  var pos = 0

  def next = {
    if (pos == elements.length) 
      pos = 0
    val value = elements(pos)
    pos = pos + 1
    value
  }

  def hasNext = !elements.isEmpty
  def add(a: A): Unit = { elements += a }
  override def toString = elements.toString

}

You can use it like this:

scala> var circ = new Circular(List(1,2))
res26: Circular[Int] = Queue(1,2)
scala> circ.next
res27: Int = 1
scala> circ.next
res28: Int = 2
scala> circ.next
res29: Int = 1
scala> circ.add(5)
scala> circ.next
res30: Int = 2
scala> circ.next
res31: Int = 5
scala> circ
res32: Circular[Int] = Queue(1,2,5)
Magnus