我正在尝试了解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)
正如您在输出中看到的那样,没有中间结果,源将一对一传递,这怎么可能?
我无法成像,它是如何运作的。
答案 0 :(得分:3)
让我们看一下您在Stream
上使用的方法:
map
和filter
均使用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
)。