41

I want to use Stream class in scala to repeat a given list infinitely.

For example the list (1,2,3,4,5) I want to create a stream that gives me (1,2,3,4,5,1,2,3,4,5,1,2,3....)

So that I can wrap the take operation. I know this can be implemented in other ways, but I wanna do it this way for some reason, just humor me :)

So the idea is that with this infinite cycle created from some list, I can use take operation, and when it reaches the end of the list it cycles.

How do I make a stream which simply repeats a given list?

Felix
  • 8,385
  • 10
  • 40
  • 59

5 Answers5

43

Very similar to @Eastsun's, but a bit more intention revealing. Tested in Scala 2.8.

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

scala> Stream.continually(l.toStream).flatten.take(10).toList
res3: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1)

Alternatively, with Scalaz:

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

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

scala> l.toStream.repeat[Stream].join.take(10).toList
res7: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1)
retronym
  • 54,768
  • 12
  • 155
  • 168
  • 1
    I liked this answer the best, continually on the Stream companion object is really what I was looking for. Also combined with flatten and tolist I get exactly what I want :) BTW, somestream.join.take(10).toList, can someone elaborate on the need for object.function() notation and why it is needed here? Usually you can have "seq take 10", and would something like "seq take 10.toList" work? – Felix Jan 21 '10 at 12:23
  • 1
    I think it's worth noting that "Stream.continually()" was apparently not present in previous releases from 2.8 – Felix Jan 21 '10 at 12:29
  • 1
    In 2.7, `Stream.continually` was `Stream.const` and `streams.flatten` was `Stream.concat(streams)` – retronym Jan 21 '10 at 12:35
  • Perhaps you could move the comparison of method invocation with and without the '.' to a new question. – retronym Jan 21 '10 at 12:37
  • for the record `Stream.concat()` doesn't do the job, that was what I tried first. concat processes its arguments eagerly, so it never returns the resulting stream – Silly Freak Jan 17 '15 at 21:34
  • `flatten` works on Travesables so `l` should be enough instead of `l.toStream`. Great answer BTW it works with `Iterable.continually` too! – Filippo Vitale Mar 16 '15 at 04:07
  • 2
    I think it's worth noting that the `Stream.continually(...).flatten` approach results in a repeating structure with unbounded potential space consumption, not a cyclic structure that fits in constant space (like Volcan's answer). – Seth Tisue Jul 20 '15 at 14:40
34

An alternative method is concatenating the .toStream of the input with itself recursively. That is,

scala> def xs: Stream[Int] = List(1, 2, 3).toStream #::: xs
xs: Stream[Int]

scala> xs.take(10).toList
res1: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1)
László van den Hoek
  • 3,955
  • 1
  • 23
  • 28
Volkan Yazıcı
  • 1,530
  • 1
  • 15
  • 28
9

There is a simple way with Stream#flatten in scala 2.8

Welcome to Scala version 2.8.0.r20542-b20100116020126 (Java HotSpot(TM) Client VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def cycle[T](seq: Seq[T]) = Stream.from(0).flatten(_ => seq)
cycle: [T](seq: Seq[T])scala.collection.immutable.Stream[T]

scala> cycle(1::2::3::Nil)
res0: scala.collection.immutable.Stream[Int] = Stream(1, ?)

scala> res0.take(10)
res1: scala.collection.immutable.Stream[Int] = Stream(1, ?)

scala> res0.take(10).toList
res2: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1)
Eastsun
  • 18,526
  • 6
  • 57
  • 81
  • Stream.from(0) is just to provide an infinite stream of objects - any other infinite stream of objects of any type would work. You then call flatten(asTraversable) to turn each object (no matter what it is) into a stream. In this case, asTraversable turns the object into the original sequence seq. It's an interesting way to do seq ++ seq ++ seq... – James Moore Nov 12 '11 at 22:37
5

Here's an implementation which doesn't assume that length is efficient:

def rep[A](seq: Seq[A]) = {
  def inner(proj: Seq[A]): Stream[A] = {
    if (proj.isEmpty)
      inner(seq)
    else
      Stream.cons(proj.first, inner(proj drop 1))
  }

  if (seq.isEmpty)
    Stream.empty
  else
    inner(seq)
}

This should run in constant time for any Seq (including List or even Stream) and only imposes a constant time overhead to populate each element. Also, it works even for infinite sequences. So, you can call rep on an infinite Stream and the resulting Stream will be equivalent to the input.

Daniel Spiewak
  • 54,515
  • 14
  • 108
  • 120
1

Stolen blatently from the excellent Scala by Example book, chapter 12, and with a few modifications:

def repeatedSeq(idx: Int, lst:Seq[Int]): Stream[Int] = Stream.cons(lst(idx), repeatedSeq((idx + 1)%lst.length, lst))

for(i <- repeatedSeq(1,List(1,1,2,3,5))) println(i)

This works for all Seq types (unless they can't be read from multiple times, of course). Might not be efficient if the .length call is slow. Tested in Scala 2.7.7.

Justin W
  • 476
  • 6
  • 16
  • 2
    "unless they can't be read from multiple times, of course" But when you're talking about sequences, you should always assume that this is the case. – James Moore May 12 '10 at 21:14