为了在Scala 2.10中尝试新的值类,我想我会按照Ruby提供的方式向Int添加一个times
方法,这样可以轻松地重复几次代码块。所以我写了这个:
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 ?
}
如果你遇到困惑,你可能会问自己,上面会产生什么输出。它不产生我期望的输出(在上面的评论中)。
正如您所看到的,有两种方法可以使重复执行的代码可以使用索引。在我们进入StringBuilder测试之前,输出看起来很好:
111
321
3
java.lang.StringIndexOutOfBoundsException: String index out of range: 3
我部分了解发生了什么,但我有一些问题。到目前为止我已经想到的是,虽然我认为3 times { s += 'x' }
会导致times
的版本A被调用,但事实上版本B被调用了。事实上,如果我删除B(并注释掉第二个测试),则会在最后一次测试中调用A并且工作正常。
我猜测{ s += 'x' }
含糊不清,可以解释为满足=> Any
的代码块,也可以解释为评估后的结果的表达式{ {1}}。也就是说,Int => Any
返回StringBuilder本身,而StringBuilder有一个s += 'x'
方法,允许您获取特定字符。编译器在调用apply( ix:Int )
之前执行后一种解释并执行块中的代码,然后将结果(StringBuilder本身)传递给times
,然后尝试三次索引。由于StringBuilder此时只有一个字符,times
会导致异常。
首先,有人可以确认/纠正/阐明上述内容吗?用于描述这个的正确术语是什么?
其次,这里有什么合适的解决方案?我唯一想到的就是通过重命名其中一种方法来避免歧义。
答案 0 :(得分:2)
正确,scala的Stringbuilder上的apply
方法返回索引处的字符。调用此方法是因为Stringbuilder
可以强制转换为f:Int => Any
,例如以下内容将编译:
val s2: (Int => Any) = s += 'x'
为了解决这个问题,我能想到的最好方法是显式输入匿名函数:
3 times { _:Any => s += 'x' };