[英]What is the intuition behind recursive algorithms with Streams?
就像標題所說的那樣,遞歸算法背后的直覺是什么:
val fibs: LazyList[Int] = (0 #:: fibs).scanLeft(1)(_ + _)
和
val fibs: LazyList[Int] = 0 #:: 1 #:: (fibs.zip(fibs.tail).map{ t => t._1 + t._2 })
它們是如何展開的? 這種算法的基本情況是什么(如果是Nil
,為什么會這樣?)以及它們如何向fibs.take(5)
發展,例如?
編輯。
正如下面幾個人指出的那樣,我確實理解延遲定義的 Stream 沒有基本案例。 相反,我的問題涉及在fibs.take(5)
中評估無限流時的基本情況是什么(我相信答案是Nil
,如果我錯了,請糾正我)以及評估fibs.take(5)
的計算步驟是什么fibs.take(5)
據說這里有兩件事在起作用:
LazyList
API 的遞歸語法所以,讓我們從關於 API 和語法的幾句話開始:
#::
接受惰性值並將其添加到LazyList
定義之前,這里是fibs
使其定義在代碼級別上遞歸LazyList
懶惰地評估它的參數,然后緩存/記憶它們以供將來使用,讓我們立即訪問已經計算的值然而,下面的機制實際上是核心遞歸的。
讓我們以List
為例來看看什么是數據遞歸:
List(1,2,3,4)
這也可以寫成
1 :: 2 :: 3 :: 4 :: Nil
這與
( ( ( Nil.::(4) ).::(3) ).::(2) ).::(1)
你可以看到我們:
Nil
::(4, Nil)
值::(3, ::(4, Nil))
值換句話說,我們必須從一些基本案例開始,自下而上構建整個事物。 根據定義,這些值必須是有限的,並且不能用於表示(可能)無限計算的系列。
但是有一種替代方法可以讓您表達這樣的計算 - corecursion 和 codata。
使用 corecursion 你有一個元組:
例如,您可以定義無限系列的 LazyList(1, 2, 3, 4, 5, 6, ...) ,例如:
// I use case class since
// type Pair = (Int, Int => Pair)
// would be illegal in Scala
final case class Pair(value: Int, f: Int => Pair)
val f: Int => Pair = n => Pair(n + 1, f)
Pair(1, f)
然后您將獲取Pair
,從中獲取value
(最初為1
)並使用它來生成新的Pair
s( Pair(2, f)
, Pair(3, f)
,...)。
使用 corecursion 生成其值的結構將被稱為 codata(因此LazyList
可以被認為是 codata)。
與斐波那契數列相同的故事,您可以使用核心遞歸地定義它
(Int, Int)
作為值(初始化為(0, 1)
val f: (Int, Int) => Pair = { case (n, m) => Pair((m, n + m), f }
作為函數(Int, Int)
對中選擇_1
但是, LazyList
的 API 為您提供了一些不錯的工具,因此您不必手動執行此操作:
list(0)
, list(1)
等,它們不會在使用后立即被遺忘.map
、 .flatMap
.scanLeft
等方法,因此雖然在內部它可能有更復雜的類型用於 corecursion,但您只會看到您需要的最終結果顯然,根據 codata 的定義,所有這些都是懶惰地完成的:在每一步中,您只能知道到目前為止定義的值,以及如何生成下一步。
這將我們引向您的示例:
val fibs: LazyList[Int] = (0 #:: fibs).scanLeft(1)(_ + _)
您可以將其視為:
(0, f)
開頭f
接受這個0
參數,並將其與1
組合以創建(0, 1)
元組f
s 跟蹤前一個值,並將其沿當前值傳遞給傳遞給scanLeft
的函數所以如果你問我,這種算法的“基本情況”是一對值和函數返回對,一遍又一遍地運行。
它們是如何展開的?
他們沒有。 #::
函數接受一個別名參數,這意味着它是惰性求值的。
這種算法的基本情況是什么(如果是 Nil,為什么會這樣?)。
沒有“基本情況”,這些遞歸定義產生無限流:
scala> val fibs: LazyList[Int] = (0 #:: fibs).scanLeft(1)(_ + _)
val fibs: LazyList[Int] = LazyList(<not computed>)
scala> fibs.size
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
(注意"<not computed>"
標記,它暗示了懶惰)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.