用法 一
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。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。