转载自:http://hongjiang.info/scala-pitfalls-3/
了解一下for表达式转换的细节:
Why?
for表达式可以这样描述:
把 p <- generator; definition* 这句再展开,由一个 p <- generator 以及0个或多个definition组成。
这个问题的细节就在于有没有definition对for表达式在转换时的影响;也就是说 for(p <- generator) 与 for(p <- generator; definition) 是不一样的。
没有yield关键字,for((key,value) <- m) 直接翻译为:
所以最初的问题是因为for里面存着了一个definition,而导致转换过程多了一次map,也就是创建了一个新的Map[Tuple2],并把数据放入新Map,在这个过程导致了hashCode的调用。
了解一下for表达式转换的细节:
for((key,value) <- m; name=key) {
println(name)
}
这里有一个map,里面有一些自定义类型的数据,然后在用上面的for进行操作,运行时会发现上面的代码时发现key的hashCode会被调用。而使用下面的写法则没有问题:for((key,value) <- m) {
val name=key
println(name)
}
Why?
for表达式可以这样描述:
for([pattern <- generator; definition*]+; filter* )
[yield] expression
在for小括号(也可以用花括号)内部由一个或多个 p <- generator; definition* 以及0个或多个filter组成。后跟着一个可选的yield关键字,以及后续的逻辑表达。把 p <- generator; definition* 这句再展开,由一个 p <- generator 以及0个或多个definition组成。
这个问题的细节就在于有没有definition对for表达式在转换时的影响;也就是说 for(p <- generator) 与 for(p <- generator; definition) 是不一样的。
没有yield关键字,for((key,value) <- m) 直接翻译为:
m.foreach(…)
而 for((key,value) <- m; name=key) 被翻译为:m.map(…).foreach(…)
为什么中间多了一次map操作?通过一个简单例子来看:scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> val list = List("A","B","C")
list: List[String] = List(A, B, C)
scala> reify( for(e <- list; x=e;y=e) { println(x+y) } )
res5: reflect.runtime.universe.Expr[Unit] =
Expr[Unit]($read.list.map(((e) => {
val x = e;
val y = e;
Tuple3.apply(e, x, y)
}))(List.canBuildFrom).foreach(((x$1) => x$1: @unchecked match {
case Tuple3((e @ _), (x @ _), (y @ _)) => Predef.println(x.$plus(y))
})))
}))(List.canBuildFrom).foreach(((x$1) => x$1: @unchecked match {
case Tuple3((e @ _), (x @ _), (y @ _)) => Predef.println(x.$plus(y))
})))
从reify的结果可以看到,对于for(e <- list; x=e; y=e) 里面的x=e和y=e两个definition中的变量x,y与e一同用一个tuple封装起来,即map的结果得到的是一个 List[Tuple3] 类型的数据。随后再对这个新结果进行foreach。所以最初的问题是因为for里面存着了一个definition,而导致转换过程多了一次map,也就是创建了一个新的Map[Tuple2],并把数据放入新Map,在这个过程导致了hashCode的调用。