繁体   English   中英

懒惰的“获取”函数如何进一步计算 Scala 流?

[英]How does the lazy 'take' function compute the Scala stream further?

在 Martin Odersky 的“Programming in Scala”一书中,有一个计算斐波那契数列的示例,该序列从作为参数传递给函数 fibFrom 的 2 个数字开始。

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() 中?

我认为应该指出的是Stream被懒惰地评估

Stream 类实现了惰性列表,其中元素仅在需要时才进行评估。

引自scala-lang.org

fibFrom函数返回一个Stream ,所以我们知道该函数不会做任何事情,即使被调用; 只有当您尝试访问Stream时,它才会开始计算数字。
take函数也返回一个Stream并懒惰地行动。
print函数是实际调用递归并在用 15 个数字填充输出时停止的函数(如您的示例)。

您可以通过一一执行功能并查看输出来轻松检查这一点。
让我们只运行fibFrom
在此处输入图片说明
我们可以看到返回的值是一个Stream并且尚未计算数字。

现在,让我们看看take(15)做了什么:
在此处输入图片说明
与我们的第一次测试相同。

最终,执行print给了我们想要的结果,因此实际上递归地运行fibFrom直到达到 15 个数字:
在此处输入图片说明

奖励:将 Stream 转换为任何非惰性数据结构将触发计算:
在此处输入图片说明

a #:: fibFrom(b, a + b)

您创建了 Stream 对象,该对象的头部是 Int,尾部是函数。 Take 是 Stream 的函数,它将使用尾部和头部计算 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)
  )

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM