views:

132

answers:

3

In Scala, is there a way to get the currently evaluated items in a Stream? For example in the Stream

val s: Stream[Int] = Stream.cons(1, Stream.cons(2, Stream.cons(3, s.map(_+1))))

the method should return only List(1,2,3).

+3  A: 

Type that statement into the interactive shell and you will see that it evaluates to s: Stream[Int] = Stream(1, ?). So, in fact, the other two elements of 2 and 3 are not yet known.

As you access further elements, more of the stream is calculated. So, now put s(3) into the shell, which will return res0: Int = 2. Now put s into the shell and you will see the new value res1: Stream[Int] = Stream(1, 2, 3, 2, ?).

The only method I could find that contained the information that you wanted was, unfortunately, s.toString. With some parsing you will be able to get the elements back out of the string. This is a barely acceptable solution with just ints and I couldn't imagine any generic solution using the string parsing idea.

erisco
+5  A: 

In 2.8, there is a protected method called tailDefined that will return false when you get to the point in the stream that has not yet been evaluated.

This isn't too useful (unless you want to write your own Stream class) except that Cons itself makes the method public. I'm not sure why it's protected in Stream and not in Cons--I would think one or the other might be a bug. But for now, at least, you can write a method like so (writing a functional equivalent is left as an exercise to the reader):

def streamEvalLen[T](s: Stream[T]) = {
  if (s.isEmpty) 0
  else {
    var i = 1
    var t = s
    while (t match {
      case c: Stream.Cons[_] => c.tailDefined
      case _ => false
    }) {
      i += 1
      t = t.tail
    }
    i
  }
}

Here you can see it in action:

scala> val s = Stream.iterate(0)(_+1)
s: scala.collection.immutable.Stream[Int] = Stream(0, ?)

scala> streamEvalLen(s)
res0: Int = 1

scala> s.take(3).toList
res1: List[Int] = List(0, 1, 2)

scala> s
res2: scala.collection.immutable.Stream[Int] = Stream(0, 1, 2, ?)

scala> streamEvalLen(s)
res3: Int = 3
Rex Kerr
The method `tailDefined` is public in both `Cons` and `Empty` so I don't think it is a bug. I didn't notice that earlier. I can adapt your solution to solve my problem.
abhin4v
+2  A: 

The solution based on Rex's answer:

def evaluatedItems[T](stream: => Stream[T]): List[T] = {
  @tailrec
  def inner(s: => Stream[T], acc: List[T]): List[T] = s match {
    case Empty => acc
    case c: Cons[T] => if (c.tailDefined) {
      inner(c.tail, acc ++ List(c.head))
    } else { acc ++ List(c.head) }
  }
  inner(stream, List())
}
abhin4v