lazy 'take' 函数如何进一步计算 Scala 流?
How does the lazy 'take' function compute the Scala stream further?
在 Martin Odersky 的“Programming in Scala”一书中,有一个计算斐波那契数列的示例,该数列以 2 个数字作为参数传递给函数 fibFrom 开始。
def fibFrom(a: Int, b: Int): Stream[Int] =
a #:: fibFrom(b, a + b)
如果将方法 take() 应用于此递归函数,例如:
fibFrom(1, 1).take(15).print
输出将是:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, empty
也许这个输出对于更有经验的人来说是显而易见的,但我不明白这个方法 take() 究竟是如何让流进一步计算的。 15 是否以某种方式不明显地传递给 fibFrom()?
与
a #:: fibFrom(b, a + b)
您创建了 Stream 对象,并且该对象具有作为 Int 的 head 和作为函数的 tail。 Take 是 Stream 的函数,它将使用 tail 和 head 计算 15 个元素。你可以查看 take() 函数的源代码:
override def take(n: Int): Stream[A] = (
// Note that the n == 1 condition appears redundant but is not.
// It prevents "tail" from being referenced (and its head being evaluated)
// when obtaining the last element of the result. Such are the challenges
// of working with a lazy-but-not-really sequence.
if (n <= 0 || isEmpty) Stream.empty
else if (n == 1) cons(head, Stream.empty)
else cons(head, tail take n-1)
)
我认为应该指出的是 Stream
是惰性求值:
The class Stream implements lazy lists where elements are only
evaluated when they are needed.
fibFrom
函数返回一个 Stream
,因此我们知道该函数不会什么都不做,即使在被调用时也是如此;只有当您尝试访问 Stream
.
时,它才会开始计算数字
take
函数也 returns 一个 Stream
并且行为懒惰。
print
函数是实际调用递归并在用 15 个数字填充输出时停止的函数(如您的示例)。
您可以通过一项一项地执行函数并查看输出来轻松检查这一点。
只让 运行 fibFrom
:
我们可以看到返回值是一个Stream
,数字还没有计算出来。
现在,让我们看看 take(15)
做了什么:
与我们的第一个测试相同。
最终,执行 print
给了我们想要的结果,因此实际上 运行ning fibFrom
递归直到达到 15 个数字:
奖励:将流转换为任何非惰性数据结构将触发计算:
在 Martin Odersky 的“Programming in Scala”一书中,有一个计算斐波那契数列的示例,该数列以 2 个数字作为参数传递给函数 fibFrom 开始。
def fibFrom(a: Int, b: Int): Stream[Int] =
a #:: fibFrom(b, a + b)
如果将方法 take() 应用于此递归函数,例如:
fibFrom(1, 1).take(15).print
输出将是:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, empty
也许这个输出对于更有经验的人来说是显而易见的,但我不明白这个方法 take() 究竟是如何让流进一步计算的。 15 是否以某种方式不明显地传递给 fibFrom()?
与
a #:: fibFrom(b, a + b)
您创建了 Stream 对象,并且该对象具有作为 Int 的 head 和作为函数的 tail。 Take 是 Stream 的函数,它将使用 tail 和 head 计算 15 个元素。你可以查看 take() 函数的源代码:
override def take(n: Int): Stream[A] = (
// Note that the n == 1 condition appears redundant but is not.
// It prevents "tail" from being referenced (and its head being evaluated)
// when obtaining the last element of the result. Such are the challenges
// of working with a lazy-but-not-really sequence.
if (n <= 0 || isEmpty) Stream.empty
else if (n == 1) cons(head, Stream.empty)
else cons(head, tail take n-1)
)
我认为应该指出的是 Stream
是惰性求值:
The class Stream implements lazy lists where elements are only evaluated when they are needed.
fibFrom
函数返回一个 Stream
,因此我们知道该函数不会什么都不做,即使在被调用时也是如此;只有当您尝试访问 Stream
.
时,它才会开始计算数字
take
函数也 returns 一个 Stream
并且行为懒惰。
print
函数是实际调用递归并在用 15 个数字填充输出时停止的函数(如您的示例)。
您可以通过一项一项地执行函数并查看输出来轻松检查这一点。
只让 运行 fibFrom
:
我们可以看到返回值是一个Stream
,数字还没有计算出来。
现在,让我们看看 take(15)
做了什么:
与我们的第一个测试相同。
最终,执行 print
给了我们想要的结果,因此实际上 运行ning fibFrom
递归直到达到 15 个数字:
奖励:将流转换为任何非惰性数据结构将触发计算: