用法 一
final class LazyList[+A] extends AbstractSeq[A] with LinearSeq[A] with LinearSeqOps[A, LazyList, LazyList[A]] with IterableFactoryDefaults[A, LazyList] with Serializable
這個類實現了一個不可變的鏈表。我們稱其為"lazy",因為它僅在需要時計算其元素。
元素被 memory ;也就是說,每個元素的值最多計算一次。
元素被計算in-order並且永遠不會被跳過。換句話說,訪問尾部會導致首先計算頭部。
LazyList
有多懶惰?當您有一個類型為 LazyList
的值時,您還不知道列表是否為空。如果你知道它是非空的,那麽你也知道頭部已經被計算了。但尾巴本身就是 LazyList
,其 emptiness-or-not 可能仍未確定。
LazyList
可能是無限的。例如,LazyList.from(0)
包含所有自然數 0、1、2 等。對於無限序列,某些方法(例如 count
、 sum
、 max
或 min
)不會終止。
這是一個例子:
import scala.math.BigInt
object Main extends App {
val fibs: LazyList[BigInt] =
BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map{ n => n._1 + n._2 }
fibs.take(5).foreach(println)
}
// prints
//
// 0
// 1
// 1
// 2
// 3
為了說明,讓我們在定義 fibs
中添加一些輸出,這樣我們就可以看到發生了什麽。
import scala.math.BigInt
object Main extends App {
val fibs: LazyList[BigInt] =
BigInt(0) #:: BigInt(1) #::
fibs.zip(fibs.tail).map{ n =>
println(s"Adding ${n._1} and ${n._2}")
n._1 + n._2
}
fibs.take(5).foreach(println)
fibs.take(6).foreach(println)
}
// prints
//
// 0
// 1
// Adding 0 and 1
// 1
// Adding 1 and 1
// 2
// Adding 1 and 2
// 3
// And then prints
//
// 0
// 1
// 1
// 2
// 3
// Adding 2 and 3
// 5
請注意,fibs
的定義使用 val
而不是 def
。 LazyList
的 memory 要求我們有一個地方來存儲信息,而 val
允許我們這樣做。
關於 LazyList
語義的進一步說明:
- 盡管LazyList
在被訪問時會發生變化,但這並不與它的不變性相矛盾。一旦記住這些值,它們就不會改變。尚未 memory 的值仍然是"exist",它們隻是還沒有被計算出來。
- 謹記備忘;如果你不小心,它會吃掉內存。這是因為 LazyList
的 memory 創建了一個很像 scala.collection.immutable.List 的結構。隻要有東西抓住頭部,頭部就會抓住尾巴,以此類推。另一方麵,如果頭部沒有任何東西(例如,如果我們使用 def
來定義 LazyList
),那麽一旦不再直接使用它,它就會消失。
- 請注意,某些操作,包括 drop 、dropWhile 、flatMap 或 collect 可能會在返回之前處理大量中間元素。
這是另一個例子。讓我們從自然數開始並迭代它們。
// We'll start with a silly iteration
def loop(s: String, i: Int, iter: Iterator[Int]): Unit = {
// Stop after 200,000
if (i < 200001) {
if (i % 50000 == 0) println(s + i)
loop(s, iter.next(), iter)
}
}
// Our first LazyList definition will be a val definition
val lazylist1: LazyList[Int] = {
def loop(v: Int): LazyList[Int] = v #:: loop(v + 1)
loop(0)
}
// Because lazylist1 is a val, everything that the iterator produces is held
// by virtue of the fact that the head of the LazyList is held in lazylist1
val it1 = lazylist1.iterator
loop("Iterator1: ", it1.next(), it1)
// We can redefine this LazyList such that all we have is the Iterator left
// and allow the LazyList to be garbage collected as required. Using a def
// to provide the LazyList ensures that no val is holding onto the head as
// is the case with lazylist1
def lazylist2: LazyList[Int] = {
def loop(v: Int): LazyList[Int] = v #:: loop(v + 1)
loop(0)
}
val it2 = lazylist2.iterator
loop("Iterator2: ", it2.next(), it2)
// And, of course, we don't actually need a LazyList at all for such a simple
// problem. There's no reason to use a LazyList if you don't actually need
// one.
val it3 = new Iterator[Int] {
var i = -1
def hasNext = true
def next(): Int = { i += 1; i }
}
loop("Iterator3: ", it3.next(), it3)
- 在前麵的fibs
示例中,tail
完全有效的事實令人感興趣。 fibs
有一個初始的 (0, 1, LazyList(...))
,所以 tail
是確定性的。如果我們定義 fibs
使得隻有 0
是具體已知的,那麽確定 tail
的行為將需要評估 tail
,因此計算將無法進行,如以下代碼所示:
// The first time we try to access the tail we're going to need more
// information which will require us to recurse, which will require us to
// recurse, which...
lazy val sov: LazyList[Vector[Int]] = Vector(0) #:: sov.zip(sov.tail).map { n => n._1 ++ n._2 }
上麵 fibs
的定義創建的對象數量超出了必要的數量,具體取決於您可能希望如何實現它。以下實現提供了更多的"cost effective" 實現,因為它有更直接的路由到數字本身:
lazy val fib: LazyList[Int] = {
def loop(h: Int, n: Int): LazyList[Int] = h #:: loop(n, h + n)
loop(1, 1)
}
頭部、尾部以及列表是否為空最初可能是未知的。一旦其中任何一個被評估,它們都是已知的,但如果尾部是用 #::
或 #:::
構建的,它的內容仍然不會被評估。相反,評估尾部內容被推遲到評估尾部空狀態,頭部或尾部。
延遲評估 LazyList 是否為空,直到需要它允許 LazyList 在調用 filter
時不即刻地評估任何元素。
隻有當它被進一步評估時(可能永遠不會!)任何元素都會被強製執行。
例如:
def tailWithSideEffect: LazyList[Nothing] = {
println("getting empty LazyList")
LazyList.empty
}
val emptyTail = tailWithSideEffect // prints "getting empty LazyList"
val suspended = 1 #:: tailWithSideEffect // doesn't print anything
val tail = suspended.tail // although the tail is evaluated, *still* nothing is yet printed
val filtered = tail.filter(_ => false) // still nothing is printed
filtered.isEmpty // prints "getting empty LazyList"
類型參數:
- A
此惰性列表中包含的元素的類型。
也可以看看:
"Scala's Collection Library overview" section on
LazyLists
for more information.伴生:
- object
源碼:
- LazyList.scala
用法 二
object LazyList extends SeqFactory[LazyList]
此對象提供一組操作來創建 LazyList
值.
伴生:
- class
源碼:
- LazyList.scala
相關用法
- Scala immutable.List用法及代碼示例
- Scala immutable.TreeMap用法及代碼示例
- Scala immutable.SortedMap用法及代碼示例
- Scala immutable.NumericRange用法及代碼示例
- Scala immutable.Range用法及代碼示例
- Scala immutable TreeSet toSeq()用法及代碼示例
- Scala immutable TreeSet sum()用法及代碼示例
- Scala immutable TreeSet init()用法及代碼示例
- Scala immutable TreeSet mkString()用法及代碼示例
- Scala immutable TreeSet diff()用法及代碼示例
- Scala immutable TreeSet toString()用法及代碼示例
- Scala immutable TreeSet find()用法及代碼示例
- Scala immutable TreeSet splitAt()用法及代碼示例
- Scala immutable TreeSet min()用法及代碼示例
- Scala immutable TreeSet toBuffer()用法及代碼示例
- Scala immutable TreeSet dropWhile()用法及代碼示例
- Scala immutable TreeSet count()用法及代碼示例
- Scala immutable TreeSet exists()用法及代碼示例
- Scala immutable TreeSet copyToArray()用法及代碼示例
- Scala immutable TreeSet toArray()用法及代碼示例
- Scala immutable TreeSet head()用法及代碼示例
- Scala immutable TreeSet map()用法及代碼示例
- Scala immutable TreeSet forall()用法及代碼示例
- Scala immutable TreeSet clone()用法及代碼示例
- Scala immutable TreeSet foreach()用法及代碼示例
注:本文由純淨天空篩選整理自scala-lang.org大神的英文原創作品 immutable.LazyList。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。