Iterating circular way
Asked Answered
H

4

13

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?

Hydrokinetic answered 15/7, 2010 at 13:52 Comment(0)
S
6

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)
Supporter answered 15/7, 2010 at 22:25 Comment(0)
L
19

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
Loricate answered 15/7, 2010 at 14:3 Comment(1)
Your Streams are infinite, but they are not circular. To make them circular you need a trick like def circular[T](xs: Stream[T]): Stream[T] = { lazy val knot: Stream[T] = xs #::: knot; knot }Blueberry
R
10
def forever:Stream[Int] = Stream(1,2,3) append forever
Rearrange answered 15/7, 2010 at 21:34 Comment(1)
Note this is infinite, but not circular.Blueberry
S
7

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.

Starcrossed answered 15/7, 2010 at 15:47 Comment(1)
You don’t need an argument for Stream.continually(), i think using a Stream[Unit] looks a little less confusing than a Stream[Int].Unpack
S
6

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)
Supporter answered 15/7, 2010 at 22:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.