これは通常のSO質問に反していることを認識していますが、次のコードはうまくいかないと思っても機能します。以下はwhileループで継続を使用する小さなScalaプログラムです。継承継承スタイルの私の理解によると、このコードは、whileループの繰り返しごとにフレームをスタックに追加することによってスタックオーバーフローエラーを生成するはずです。しかし、それは正常に動作します。whileループでのスカラ継続の使用
import util.continuations.{shift, reset}
class InfiniteCounter extends Iterator[Int] {
var count = 0
var callback: Unit=>Unit = null
reset {
while (true) {
shift {f: (Unit=>Unit) =>
callback = f
}
count += 1
}
}
def hasNext: Boolean = true
def next(): Int = {
callback()
count
}
}
object Experiment3 {
def main(args: Array[String]) {
val counter = new InfiniteCounter()
println(counter.next())
println("Hello")
println(counter.next())
for (i <- 0 until 100000000) {
counter.next()
}
println(counter.next())
}
}
出力は次のようになります。
1
Hello
2
100000003
私の質問は:なぜ何のスタックオーバーフローが存在しませんか? Scalaコンパイラは、テールコールの最適化を行っていますか(継続ではできないと思っていました)、あるいは別のことが起こっていますか?
(この実験は、それを実行するために必要なSBTの設定と一緒にgithubの上にある:。https://github.com/jcrudy/scala-continuation-experimentsを7cec9befcf58820b925bb222bc25f2a48cbec4a6をコミット参照)
私はそれがかなり混乱しているものの大きな説明だと思います。私はこれを視覚的にスケッチしようとするかもしれない。これを明確にするために、この構成はスタックを吹き飛ばすだけでなく、全体のメモリ要件も控えめであり、「反復回数」に依存しないことを意味します。 – jcrudy
@jcrudy - はい、メモリ要件は 'for'式の繰り返し回数に依存しません。簡単なテストとして、私はあなたのコードを次のように実行しました: 'JAVA_OPTS =" - Xmx2M -verbose:gc "scala Experiment3'(私のラップトップで動作します)これは、100Mの反復がちょうど2MBのヒープ空間で動作することを証明しています。冗長ガベージコレクションのオプションを追加することで、実際のメモリ使用量が実行中ほぼ一定に保たれることがわかります。 – DaoWen
ScalaのトランポリンにRich Doughertyが投稿した素敵なイラストのブログ記事があります:http://blog.richdougherty.com/2009/04/tail-calls-tailrec-and-trampolines.html – jcrudy