簡體   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