在回答堆叠的问题问题时,我创建了一个流的流,就像这样:
val s:Stream[Int] = 1 #:: s.map(_*2)
有人告诉我, def 应该使用 val ,因为Scala Kata抱怨(Eclipse中的Scala工作表),即"正向参考"扩展到定义上。价值s。"
但是流文档中的示例使用val。哪一个是对的?
scalac,只要变量是类而不是局部变量的字段,就可以使用该代码(使用val)。您可以使变量懒惰以满足Scala Kata,但是通常不想在真实程序中以这种方式使用DEF(即以本身为单位的DEF)。如果这样做,每次调用该方法时都会创建一个新的流,因此永远无法重复使用以前的计算(保存在流中)的结果(保存在流中)。如果您使用此类流中的许多值,性能将是可怕的,最终您将用完存储器。
此程序以这种方式说明了使用DEF的问题:
// Show the difference between the use of val and def with Streams.
object StreamTest extends App {
def sum( p:(Int,Int) ) = { println( "sum " + p ); p._1 + p._2 }
val fibs1: Stream[Int] = 0 #:: 1 #:: ( fibs1 zip fibs1.tail map sum )
def fibs2: Stream[Int] = 0 #:: 1 #:: ( fibs2 zip fibs2.tail map sum )
println("========== VAL ============")
println( "----- Take 4:" ); fibs1 take 4 foreach println
println( "----- Take 5:" ); fibs1 take 5 foreach println
println("========== DEF ============")
println( "----- Take 4:" ); fibs2 take 4 foreach println
println( "----- Take 5:" ); fibs2 take 5 foreach println
}
这是输出:
========== VAL ============
----- Take 4:
0
1
sum (0,1)
1
sum (1,1)
2
----- Take 5:
0
1
1
2
sum (1,2)
3
========== DEF ============
----- Take 4:
0
1
sum (0,1)
1
sum (0,1)
sum (1,1)
2
----- Take 5:
0
1
sum (0,1)
1
sum (0,1)
sum (1,1)
2
sum (0,1)
sum (0,1)
sum (1,1)
sum (1,2)
3
请注意,当我们使用Val时:
- " take 5"没有重新计算" take 4"计算的值。
- 计算" take 4"中的第四值不会导致重新计算第三值。
,但是当我们使用DEF时,这些都不是真实的。该流的每一次使用,包括其自身的递归,都从新的流中从头开始。由于产生n个值要求我们首先产生n-1和n-2的值,每个值必须产生自己的两个前任,依此类推,因此要产生一个值的sum()数量都非常类似于斐波那契序列本身:0、0、1、2、4、7、12、20、33,......并且由于所有这些流都同时在堆上,因此我们很快就用完了记忆。<<<<<<<<<<<</p>
因此,鉴于性能和内存问题较差,您通常不想在创建流中使用DEF。
,但可能是您实际上每次都需要一个新的流。假设您需要一个随机整数流,并且每次访问流的流时,您都需要新整数,而不是先前计算的整数的重播。而且那些先前计算的值(由于您不想重复使用它们)将不必要地占用堆的空间。在这种情况下,使用DEF是有意义的,因此您每次都会获得新的流并不坚持下去,以便可以收集垃圾:
scala> val randInts = Stream.continually( util.Random.nextInt(100) )
randInts: scala.collection.immutable.Stream[Int] = Stream(1, ?)
scala> ( randInts take 1000 ).sum
res92: Int = 51535
scala> ( randInts take 1000 ).sum
res93: Int = 51535 <== same answer as before, from saved values
scala> def randInts = Stream.continually( util.Random.nextInt(100) )
randInts: scala.collection.immutable.Stream[Int]
scala> ( randInts take 1000 ).sum
res94: Int = 49714
scala> ( randInts take 1000 ).sum
res95: Int = 48442 <== different Stream, so new answer
使randints成为方法会使我们每次都获得新的流,因此我们获得了新值,并且可以收集流。
请注意,在此处使用DEF只是有意义的,因为新值不依赖旧值,因此randints并未根据本身定义。Stream.continually
是一种生产此类流的简便方法:您只告诉它如何制造价值,并为您制作流。