开发者

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

开发者_JAVA百科

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).


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


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())
}


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.


Using scanLeft

    lazy val s: Stream[Int] = 1 #:: s.scanLeft(2) { case (a, _) => 1 + a }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜