3

为了尝试 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 ?
}

如果您喜欢谜题,此时您可能会问自己上面的输出会产生什么。它不会产生我期望的输出(在上面的评论中)。

如您所见,有两种方法可以使要重复执行的代码可以使用索引或不使用索引。在我们进行 StringBuilder 测试之前,输出看起来不错:

111
321
3
java.lang.StringIndexOutOfBoundsException: String index out of range: 3

部分了解发生了什么,但我有一些问题。到目前为止我发现的是,虽然我认为这3 times { s += 'x' }会导致版本 Atimes被调用,但实际上版本 B 被调用了。实际上,如果我删除 B(并注释掉第二个测试),则会为最后一个测试调用 A 并且可以正常工作。

我猜这{ s += 'x' }是模棱两可的,可以解释为满足的代码块或评估后结果为 type=> Any的表达式。也就是说,返回 StringBuilder 本身,而 StringBuilder 有一个方法可以让你获取特定的字符。编译器进行后一种解释并在调用之前执行块中的代码一次,然后将结果(StringBuilder 本身)传递给,然后尝试对其进行索引三次。由于此时 StringBuilder 中只有一个字符,因此会导致异常。Int => Anys += 'x'apply( ix:Int )timestimessb.apply(3)

那么首先,有人可以确认/纠正/阐明上述内容吗?描述这一点的正确术语是什么?

其次,这里的适当修复是什么?我唯一想到的是通过重命名其中一种方法来避免歧义。

4

1 回答 1

2

正确,applyscala 的Stringbuilder上的方法返回索引处的字符。这会被调用,因为Stringbuilder可以转换f:Int => Any为例如以下将编译:

val s2: (Int => Any) = s += 'x'

为了解决这个问题,我能想到的最好方法是显式键入匿名函数:

3 times { _:Any => s += 'x' };
于 2013-03-24T12:21:15.707 回答