[英]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 類實現了惰性列表,其中元素僅在需要時才進行評估。
fibFrom
函數返回一個Stream
,所以我們知道該函數不會做任何事情,即使被調用; 只有當您嘗試訪問Stream
時,它才會開始計算數字。
take
函數也返回一個Stream
並懶惰地行動。
print
函數是實際調用遞歸並在用 15 個數字填充輸出時停止的函數(如您的示例)。
您可以通過一一執行功能並查看輸出來輕松檢查這一點。
讓我們只運行fibFrom
:
我們可以看到返回的值是一個Stream
並且尚未計算數字。
現在,讓我們看看take(15)
做了什么:
與我們的第一次測試相同。
和
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.