2015年的金山岭长城马拉松

上个周日(4.19)参加了金山岭马拉松(半程),这是我参与过的难度最大的一个马拉松,用了4小时59分钟才完成,比2012年参加过的杭州全程马拉松用时还长。21公里路程里约三分之二是在长城上的,在长城上的上坡阶段基本是手脚并用,真正是“爬”长城。

当然这种巨大的艰辛也是值得的,金山岭长城的游人很少,山谷里的桃花,梨花,正盛开着,景色非常美。

scala雾中风景(22): var变量与赋值操作符

晚上review代码时发现的一个问题,同事写的一段代码简化后如下:

var map = scala.collection.immutable.Map[Int,Int]()
map += (1->2)
map += (3->4)
...
if (xxx) map.get(..)

这里逻辑上感觉有问题,map这里用的不可变集合,+=方法应该是返回一个新的值,后边的get应该不能生效。另外觉得 += 这个方法似乎应该只在mutable集合下才有,试图在eclipse下点击进入这个+=方法看看,果然也无法进入。

因为代码上下文逻辑较多,不确定是不是上下文有影响,于是在repl下验证一下这个方法。

scala> val m = scala.collection.immutable.Map[Int,Int]()

scala> m += (1->1)
<console>:12: error: value += is not a member of scala.collection.immutable.Map[Int,Int]
          m += (1->1)
            ^

提示说Map没有这个方法,奇怪为何那段代码在eclipse编译没有问题。然后注意到同事用的是var修饰的map

修改为var之后可以使用+=了,看一下编译器怎么翻译的:

scala> import reflect.runtime.universe._

scala> var m = Map[Int,Int]()

scala> reify ( m += (1->1) )
res4: reflect.runtime.universe.Expr[Unit] = 
        Expr[Unit]($read.m_$eq($read.m.$plus(Predef.ArrowAssoc(1).$minus$greater(1))))

原来这里+=是赋值操作符(assignment operator)而不是方法,恍然大悟,在scala下赋值操作符仅对var修饰的变量生效。m += (k->v)相当于 m = m + (k->v)Map里正好定义了+方法:

def + [B1 >: B](kv: (A, B1)): Map[A, B1]

如果没有定义过+方法的话,编译器会采用字符串连接,也就是对+前后的对象进行toString然后拼接;这种情况下会因为类型不匹配而报错。

所以他的代码也能work,只是这里使用+=操作符容易误会为是方法。建议还是采用val修饰一个可变的Map,来存放数据。

补充,对于var变量,即使以.+=方式调用,编译器也会对待为操作符:

scala> reify ( m.+=(1->1) )
res11: reflect.runtime.universe.Expr[Unit] = 
        Expr[Unit]($read.m_$eq($read.m.$plus(Predef.ArrowAssoc(1).$minus$greater(1))))

个人觉得这种情况下编译器报错似乎更合适。