Scala の例で質問しますが、命令型と関数型のハイブリッド スタイルを許可する他の言語に影響を与える可能性があります。
以下に短い例を示します ( UPDATED、以下を参照)。
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
は遅延しているため、関数literal への参照を保持しています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
}
実際、これらの仕組みが少し理解できたので、それほど心配していません。