Scala 2.10 の新しい値クラスを試すために、times
Ruby が提供するものに沿って Int にメソッドを追加し、コード ブロックを数回簡単に繰り返せるようにしようと考えました。だから私はこれを書いた:
object Test extends App {
implicit class Int_times( val n:Int ) extends AnyVal {
def times( f: => Any ) { var i = n; while ( i>0 ) { f ; i -= 1 } } // A
def times( f:Int => Any ) { var i = n; while ( i>0 ) { f(i); i -= 1 } } // B
}
3 times print(1) ; println // 111 ?
3 times { print(_) }; println // 321 ?
var x = 0 ; 3 times { x += 1 }; println(x) // 3 ?
var s = new StringBuilder; 3 times { s += 'x' }; println(s) // xxx ?
}
謎解きが好きな人は、この時点で、上記の結果がどのような出力になるかを自問するかもしれません。期待した出力が生成されません(上記のコメントで)。
ご覧のように、繰り返し実行されるコードがインデックスを利用するかどうかの 2 つの方法があります。StringBuilder テストに到達するまで、出力は良好に見えます。
111
321
3
java.lang.StringIndexOutOfBoundsException: String index out of range: 3
何が起こっているのか部分的に理解していますが、いくつか質問があります。3 times { s += 'x' }
これまでにわかったことは、バージョン Aが呼び出されると思っていたのにtimes
、実際にはバージョン B が呼び出されたということです。実際、B を削除 (および 2 番目のテストをコメント アウト) すると、最後のテストで A が呼び出され、完全に機能します。
それはあいまいで、満足するコードブロックとして、または評価後の結果が type を持つ式{ s += 'x' }
として解釈できると思います。つまり、StringBuilder 自体を返します。StringBuilder には、特定の文字を取得できるメソッドがあります。コンパイラは後者の解釈を行い、を呼び出す前にブロック内のコードを1 回実行し、結果 (StringBuilder 自体) を に渡します。この時点で StringBuilder には 1 文字しかないため、例外が発生します。=> Any
Int => Any
s += 'x'
apply( ix:Int )
times
times
sb.apply(3)
まず、誰かが上記を確認/修正/解明してもらえますか? これを説明する際に使用する適切な用語は何ですか?
第二に、ここで適切な修正は何ですか? 私に起こる唯一のことは、メソッドの1つの名前を変更してあいまいさを避けることです。