Scala - 为什么我应该用关键字 lazy 定义一个 Stream?
Scala - Why should I define a Stream with the keyword lazy?
Streams
根据定义是惰性集合类型。但是当我查看使用Streams
的示例时,似乎我在定义它们时仍然必须使用关键字lazy
。
示例:
lazy val myStream: Stream[Int] = 2 #:: Stream.empty
为什么我需要这样做?当我执行以下命令时,我得到相同的结果:
val myStream: Stream[Int] = 2 #:: Stream.empty
如果没有看到您所指的具体示例,很难说太多,但使用 lazy
的原因之一是推迟对流头部的评估。例如,注意这之间的区别:
lazy val myStream: Stream[Int] =
{ println("Evaluating head!"); 2} #:: Stream.empty
还有这个:
val myStream: Stream[Int] =
{ println("Evaluating head!"); 2} #:: Stream.empty
在第二种情况下,将立即打印消息,因为 #::
评估流的头部。在任何情况下,您在定义流时都不会 "have to" 使用 lazy
— 通常立即评估头部不是问题,或者实际上是您想要的行为。
您没有将Streams
声明为lazy
。由于几个原因,它是惯用的。
首先,Streams
用于我们希望集合尽可能惰性的时候。使用 lazy
甚至可以延迟计算 Stream
的头部:
def myExpensiveOperation = { println("computing..."); Thread.sleep(5000); 1 }
//Just declaring this stream variable causes the thread to sleep,
//Even though we might not ever need to iterate through the stream
val stream = myExpensiveOperation #:: Stream.empty
//Declaring the stream as lazy val solves this issue:
lazy val stream = myExpensiveOperation #:: Stream.empty
这样也可以防止异常:
val stream = (1/0) #:: Stream.empty //Throws exception
lazy val stream = (1/0) #:: Stream.empty //Safe
另一个问题是记忆。为了尽可能延迟对许多数据类型的执行,使用 def
代替 lazy val
是很自然的:
def streamDef = myExpensiveOperation #:: Stream.empty //No sleeping or printing!
这里的问题是 Streams
很聪明地记住了他们的结果,但这需要我们有地方存储信息,而 val
允许我们这样做。所以如果我们这样做:
streamDef.toList //Sleep and print
streamDef.toList //Sleep and print again!
streamVal.toList //Sleep and print
streamVal.toList //No sleeping or printing! The results have been memoized.
所以本质上 lazy val
为我们提供了 def
的最终延迟执行,同时保留了 val
的记忆能力。您可以在 docs 中阅读有关 Streams
记忆的更多详细信息。
Streams
根据定义是惰性集合类型。但是当我查看使用Streams
的示例时,似乎我在定义它们时仍然必须使用关键字lazy
。
示例:
lazy val myStream: Stream[Int] = 2 #:: Stream.empty
为什么我需要这样做?当我执行以下命令时,我得到相同的结果:
val myStream: Stream[Int] = 2 #:: Stream.empty
如果没有看到您所指的具体示例,很难说太多,但使用 lazy
的原因之一是推迟对流头部的评估。例如,注意这之间的区别:
lazy val myStream: Stream[Int] =
{ println("Evaluating head!"); 2} #:: Stream.empty
还有这个:
val myStream: Stream[Int] =
{ println("Evaluating head!"); 2} #:: Stream.empty
在第二种情况下,将立即打印消息,因为 #::
评估流的头部。在任何情况下,您在定义流时都不会 "have to" 使用 lazy
— 通常立即评估头部不是问题,或者实际上是您想要的行为。
您没有将Streams
声明为lazy
。由于几个原因,它是惯用的。
首先,Streams
用于我们希望集合尽可能惰性的时候。使用 lazy
甚至可以延迟计算 Stream
的头部:
def myExpensiveOperation = { println("computing..."); Thread.sleep(5000); 1 }
//Just declaring this stream variable causes the thread to sleep,
//Even though we might not ever need to iterate through the stream
val stream = myExpensiveOperation #:: Stream.empty
//Declaring the stream as lazy val solves this issue:
lazy val stream = myExpensiveOperation #:: Stream.empty
这样也可以防止异常:
val stream = (1/0) #:: Stream.empty //Throws exception
lazy val stream = (1/0) #:: Stream.empty //Safe
另一个问题是记忆。为了尽可能延迟对许多数据类型的执行,使用 def
代替 lazy val
是很自然的:
def streamDef = myExpensiveOperation #:: Stream.empty //No sleeping or printing!
这里的问题是 Streams
很聪明地记住了他们的结果,但这需要我们有地方存储信息,而 val
允许我们这样做。所以如果我们这样做:
streamDef.toList //Sleep and print
streamDef.toList //Sleep and print again!
streamVal.toList //Sleep and print
streamVal.toList //No sleeping or printing! The results have been memoized.
所以本质上 lazy val
为我们提供了 def
的最终延迟执行,同时保留了 val
的记忆能力。您可以在 docs 中阅读有关 Streams
记忆的更多详细信息。