简体   繁体   English

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

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

In Martin Odersky's book “Programming in Scala” there is an example of computing the Fibonacci sequence starting with 2 numbers passed as arguments to the function fibFrom.在 Martin Odersky 的“Programming in Scala”一书中,有一个计算斐波那契数列的示例,该序列从作为参数传递给函数 fibFrom 的 2 个数字开始。

def fibFrom(a: Int, b: Int): Stream[Int] =
       a #:: fibFrom(b, a + b)

If you apply the method take() to this recursive function like:如果您将方法 take() 应用于此递归函数,例如:

fibFrom(1, 1).take(15).print

The output will be:输出将是:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, empty

Maybe this output is obvious for more experienced people, but I don't understand how exactly this method take() makes the stream to be calculated further.也许这个输出对于更有经验的人来说是显而易见的,但我不明白这个方法 take() 究竟是如何使流被进一步计算的。 Is 15 somehow nonobviously passed into fibFrom()? 15 是否以某种方式不明显地传递到 fibFrom() 中?

I think the thing that should be pointed is that Stream is lazily evaluated :我认为应该指出的是Stream被懒惰地评估

The class Stream implements lazy lists where elements are only evaluated when they are needed. Stream 类实现了惰性列表,其中元素仅在需要时才进行评估。

Quoted from scala-lang.org引自scala-lang.org

fibFrom function is returning a Stream , so we understand that the function won't do nothing, even when called; fibFrom函数返回一个Stream ,所以我们知道该函数不会做任何事情,即使被调用; it will start calculating the numbers only when you try to access the Stream .只有当您尝试访问Stream时,它才会开始计算数字。
take function also returns a Stream and acts lazily. take函数也返回一个Stream并懒惰地行动。
The print function is the one that actually calls the recursion and stops when filling the output with 15 numbers (like your example). print函数是实际调用递归并在用 15 个数字填充输出时停止的函数(如您的示例)。

You can easily check this by performing the functions one by one and see the output.您可以通过一一执行功能并查看输出来轻松检查这一点。
Lets run only fibFrom :让我们只运行fibFrom
在此处输入图片说明
We can see that the returned value is a Stream and the numbers are not calculated yet.我们可以看到返回的值是一个Stream并且尚未计算数字。

Now, lets see what take(15) does:现在,让我们看看take(15)做了什么:
在此处输入图片说明
Same as our first test.与我们的第一次测试相同。

Eventually, performing print is giving us the desired result thus actually running fibFrom recursively until reaching 15 numbers:最终,执行print给了我们想要的结果,因此实际上递归地运行fibFrom直到达到 15 个数字:
在此处输入图片说明

Bonus: Converting the Stream to any non lazy data structure will trigger the computations:奖励:将 Stream 转换为任何非惰性数据结构将触发计算:
在此处输入图片说明

With

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

you created Stream object, and that object has head which is Int and tail which is function.您创建了 Stream 对象,该对象的头部是 Int,尾部是函数。 Take is function of Stream that will calculate 15 elements using tail and head. Take 是 Stream 的函数,它将使用尾部和头部计算 15 个元素。 You can check source code of take() function:您可以查看 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