在Scala中,有没有办法在Stream中获取当前评估的项目?

时间:2010-05-14 05:20:03

标签: scala stream lazy-evaluation

在Scala中,有没有办法在Stream中获取当前评估的项目?例如在Stream

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

该方法应仅返回List(1,2,3)

4 个答案:

答案 0 :(得分:8)

在2.8中,有一个名为tailDefined的受保护方法,当您到达尚未评估的流中的点时,它将返回false。

这不太有用(除非你想编写自己的Stream类),除了Cons本身使方法公开。我不确定为什么它在Stream中受到保护而不在Cons中 - 我会认为其中一个可能是一个bug。但是现在,至少,你可以写一个像这样的方法(写一个功能等价物留给读者练习):

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

在这里你可以看到它的实际效果:

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

答案 1 :(得分:6)

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

答案 2 :(得分:4)

在交互式shell中键入该语句,您将看到它的计算结果为s: Stream[Int] = Stream(1, ?)。所以,事实上,2和3的其他两个元素尚不清楚。

当您访问更多元素时,会计算更多的流。所以,现在将s(3)放入shell中,它将返回res0: Int = 2。现在将s放入shell中,您将看到新值res1: Stream[Int] = Stream(1, 2, 3, 2, ?)

我唯一能找到的包含您想要的信息的方法是s.toString。通过一些解析,您将能够从字符串中取回元素。这是一个几乎不可接受的解决方案,只是整数,我无法想象任何使用字符串解析思想的通用解决方案。

答案 3 :(得分:0)

使用scanLeft

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