在回答一個StackOverflow的問題,我創建了一個流作爲VAL,像這樣:定義流時,我應該使用val還是def?
val s:Stream[Int] = 1 #:: s.map(_*2)
,有人告訴我,高清應該用來代替VAL因爲Scala卡塔抱怨(如不Eclipse中的Scala工作表):「前向引用擴展了數值s的定義」。
但Stream文檔中的示例使用val。哪一個是對的?
在回答一個StackOverflow的問題,我創建了一個流作爲VAL,像這樣:定義流時,我應該使用val還是def?
val s:Stream[Int] = 1 #:: s.map(_*2)
,有人告訴我,高清應該用來代替VAL因爲Scala卡塔抱怨(如不Eclipse中的Scala工作表):「前向引用擴展了數值s的定義」。
但Stream文檔中的示例使用val。哪一個是對的?
只要變量是類的字段而不是局部變量,Scalac和REPL就可以使用該代碼(使用val)。你可以讓變量懶惰來滿足Scala Kata,但是你通常不希望在一個真正的程序中以這種方式使用def(也就是說,根據它本身來定義Stream)。如果這樣做,每次調用方法時都會創建一個新的Stream,所以以前的計算結果(保存在Stream中)永遠不會被重用。如果你使用這樣一個Stream中的很多值,性能會很糟糕,最終你會耗盡內存。
該程序以這種方式使用高清演示了此問題:
// 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:
但是,當我們使用def時,這兩者都不是真的。 Stream的每一次使用,包括它自己的遞歸,都是從頭開始用新的Stream開始的。由於產生第N個值需要我們首先產生N-1和N-2的值,其中每一個必須產生它自己的兩個前置值,依此類推,產生一個值所需的sum()斐波那契序列本身:0,0,1,2,4,7,12,20,33 ....因爲所有這些流同時在堆中,所以我們很快就會耗盡內存。
所以考慮到糟糕的性能和內存問題,您通常不希望在創建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
是生成這種Streams的簡單方法:您只需告訴它如何創建一個值,併爲您製作Stream。
你確定它是一個演示編譯器的限制,而不只是一個字段與本地變量的東西? –
好點,Luigi。在閱讀您的評論之後,我做了一些更多的嘗試,現在我不認爲自己完全理解問題是什麼,但我認爲它與這些工具包裝代碼的方式有關。我在Scala工作表中的一個對象中得到錯誤,但不是在一個類中,並且都與scalac一起工作。我會修改答案,不要責怪PC。 – AmigoNico
如果你用一個對象包裝它,它的工作原理就很好:http://www.scalakata.com/50975187e4b093f3524f3685 –