我會問一個Scala的例子,但很可能這會影響到其他語言,這些語言允許混合命令式和功能樣式。我應該如何避免無意中捕獲函數文字中的局部範圍?
下面是一個簡單的例子(修訂,見下文):
def method: Iterator[Int] {
// construct some large intermediate value
val huge = (1 to 1000000).toList
val small = List.fill(5)(scala.util.Random.nextInt)
// accidentally use huge in a literal
small.iterator filterNot (huge contains _)
}
現在iterator.filterNot
懶洋洋的作品,這是偉大的!因此,我們期望返回的迭代器不會消耗太多內存(實際上,O(1))。但不幸的是,我們已經有了一個可怕的錯誤:因爲filterNot
是懶惰的,它保持對函數文本huge contains _
參考。
因此,雖然我們認爲該方法在運行時需要大量內存,並且該方法終止後該內存可以立即釋放,但事實上內存會卡住,直到我們忘記返回Iterator
。
(我只是做了這樣的錯誤,這花了很長的時間來追查!你能趕上這樣的事情望着堆轉儲...)
什麼是避免這個問題的最佳做法?
看來,唯一的解決方案是仔細檢查在範圍末尾倖存的函數文字以及捕獲的中間變量。如果你正在構建一個非嚴格的集合並計劃返回它,這有點尷尬。任何人都可以考慮一些不錯的技巧,Scala特有的或其他的,以避免這個問題,讓我寫出漂亮的代碼?
更新:我以前給出的例子很愚蠢,因爲huynhjl的回答如下所示。它一直:
def method: Iterator[Int] {
val huge = (1 to 1000000).toList // construct some large intermediate value
val n = huge.last // do some calculation based on it
(1 to n).iterator map (_ + 1) // return some small value
}
事實上,現在我明白了一個好一點的這些東西是如何工作的,我不是很擔心!
咦,的確如此!我會在明天嘗試提出一個更好的例子,但現在我很困惑*爲什麼*你看到你做了什麼。任何人都可以給出一個很好的總結,哪些局部變量被該函數文字捕獲? – 2010-10-18 06:59:05