流如何传递增量?

时间:2017-05-12 11:15:08

标签: scala

我正在尝试了解Stream如何运作并遵循Stream实施:

sealed trait Stream[+A] {

  def toList: List[A] = {
    @annotation.tailrec
    def go(s: Stream[A], acc: List[A]): List[A] = s match {
      case Cons(h, t) => go(t(), h() :: acc)
      case _ => acc
    }

    go(this, List()).reverse
  }

  def foldRight[B](z: => B)(f: (A, => B) => B): B =
    this match {
      case Cons(h, t) => f(h(), t().foldRight(z)(f))
      case _ => z
    }

  def map[B](f: A => B): Stream[B] =
    this.foldRight(Stream.empty[B])((x, y) => Stream.cons(f(x), y))

  def filter(f: A => Boolean): Stream[A] =
    this.foldRight(Stream.empty[A])((h, t) => if (f(h)) Stream.cons(h, t) else t)

}

case object Empty extends Stream[Nothing]

case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]


object Stream {

  def cons[A](hd: => A, t1: => Stream[A]): Stream[A] = {
    lazy val head = hd
    lazy val tail = t1

    Cons(() => head, () => tail)
  }

  def empty[A]: Stream[A] = Empty

  def apply[A](as: A*): Stream[A] =
    if (as.isEmpty) empty else cons(as.head, apply(as.tail: _*))

}

以及使用Stream的代码:

Stream(1,2,3,4).map((x) => {
  println(x)
  x + 10
}).filter((x) => {
  println(x)
  x % 2 == 0
}).toList

作为输出我得到了:

1
11
2
12
3
13
4
14
res4: List[Int] = List(12, 14)

正如您在输出中看到的那样,没有中间结果,源将一对一传递,这怎么可能?

我无法成像,它是如何运作的。

1 个答案:

答案 0 :(得分:3)

让我们看一下您在Stream上使用的方法:

mapfilter均使用foldRight实施。为了更清楚,让我们使用引用透明度原则在foldRight内部map(使用filter也可以这样做):

def map[B](f: A => B) = this match {
  case Cons(h, t) => Stream.cons(f(h()), t().map(f))
  case _ => Empty
}

现在,此代码中f的评估位置是什么?从来没有,因为Stream.cons参数是按名称调用的,所以我们只给出新流的描述,而不是它的值。

一旦您确信这一事实,您就可以轻松地看到同样适用于filter,因此我们可以继续toList

它会评估Stream中的每个元素,并将值放在List中,最后会反转。

但是,评估已经过滤和映射的Stream元素正在精确地读取值的描述,因此在此处评估实际函数。因此控制台按顺序输出:首先为每个元素调用map函数,然后调用filter函数,一次一个(因为我们现在处于延迟映射和过滤的Stream )。