gpt4 book ai didi

scala - 定义流时应该使用 val 还是 def ?

转载 作者:行者123 更新时间:2023-12-04 02:56:21 25 4
gpt4 key购买 nike

在对 StackOverflow 问题的回答中,我创建了一个 Stream 作为 val,如下所示:

val s:Stream[Int] = 1 #:: s.map(_*2)

有人告诉我 定义 应该使用代替 val 因为 Scala Kata 提示(就像 Eclipse 中的 Scala Worksheet 一样)“前向引用扩展了 value 的定义”。

但是 Stream 文档中的示例使用 val。哪一个是对的?

最佳答案

只要变量是类的字段而不是局部变量,Scalac 和 REPL 就可以使用该代码(使用 val)。您可以使变量惰性以满足 Scala Kata 的要求,但您通常不希望在实际程序中以这种方式使用 def(即,就其本身而言 def 是一个 Stream)。如果这样做,每次调用该方法时都会创建一个新的 Stream,因此之前计算的结果(保存在 Stream 中)永远无法重用。如果您使用来自此类 Stream 的许多值,性能将很糟糕,最终您将耗尽内存。

该程序演示了以这种方式使用 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 时,这些都不是真的。 Stream 的每次使用,包括它自己的递归,都从一个新的 Stream 开始。由于生成第 N 个值需要我们首先生成 N-1 和 N-2 的值,每个值都必须生成自己的两个前辈,依此类推,生成一个值所需的 sum() 调用次数增长很像斐波那契数列本身:0, 0, 1, 2, 4, 7, 12, 20, 33, .... 由于所有这些流同时在堆上,我们很快就会耗尽内存。

    因此,鉴于性能和内存问题不佳,您通常不想在创建 Stream 时使用 def。

    但可能是您实际上每次都想要一个新的 Stream。假设您想要一个随机整数的 Stream,并且每次访问该 Stream 时,您都需要新的整数,而不是之前计算的整数的重放。而那些先前计算的值,由于您不想重用它们,会不必要地占用堆上的空间。在这种情况下,使用 def 是有意义的,这样您每次都会获得一个新的 Stream 并且不保留它,以便它可以被垃圾收集:
    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 成为方法使我们每次都获得一个新的 Stream,因此我们获得了新的值,并且可以收集该 Stream。

    请注意,这里只使用 def 才有意义,因为新值不依赖于旧值,因此 randInts 不是根据自身定义的。 Stream.continually是一种生成此类流的简单方法:您只需告诉它如何生成值,它就会为您生成一个流。

    关于scala - 定义流时应该使用 val 还是 def ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13217222/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com