Scala编程(第4版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

5.9 操作符优先级和结合性

操作符优先级决定了表达式中的哪些部分会先于其他部分被求值。例如,表达式 2 + 2 * 7求值得到16而不是28,因为操作符*的优先级高于+。因此,表达式的乘法部分先于加法部分被求值。当然也可以在表达式中用圆括号来澄清求值顺序,或者覆盖默认的优先级。例如,如果你真的想要上述表达式求值得到28,可以像这样来写:

由于Scala并不是真的有操作符,操作符仅仅是用操作符表示法使用方法的一种方式,你可能会好奇——操作符优先级的工作原理是什么?Scala根据操作符表示法中使用的方法名的首个字母来判定优先级(这个规则有一个例外,会在后面讲到)。举例来说,如果方法名以*开始,它将拥有比以+开始的方法更高的优先级。因此,2 + 2 * 7会被当作2 + (2 * 7)求值。同理,a +++ b *** c(其中abc是变量,+++***是方法)将被当作a +++(b *** c)求值,因为方法***+++的优先级更高。

表5.3 操作符优先级

表5.3显示了方法首字符的优先级顺序,依次递减,位于同一行的拥有同样的优先级。在表格中某个字符的优先级越高,那么以这个字符打头的方法就拥有更高的优先级。如下例子展示了优先级的影响:

<<方法以字符<打头,在表5.3中,<出现在字符+的下方,因此表达式会先调用+方法,然后是<<方法,即 2 << (2 + 2)。我们按数学计算,2 + 242 << 432。如果将这两个操作交换一下次序,将会得到不同的结果:

由于方法的首字符跟前一例一样,方法将会按照相同的顺序调用。先是+方法,然后是<<方法。因此2 + 24,而4 << 216

前面提到过,优先级规则的一个例外是赋值操作符assignment operator),这些操作符以等号(=)结尾,且不是比较操作符(<=>===!=),它们的优先级跟简单的赋值(=)拥有的优先级一样。也就是说,比其他任何操作符都低。例如:

跟如下代码是一样的:

因为*=被归类为赋值操作符,而赋值操作符的优先级比+低,尽管它的首字符是*,看上去应该比+的优先级更高。

当多个同等优先级的操作符并排在一起时,操作符的结合性associativity)决定了操作符的分组。Scala中操作符的结合性由操作符的最后一个字符决定。正如我们在第3章提到的,任何以“:”字符结尾的方法都是在它右侧的操作元上调用,传入左侧的操作元的。以任何其他字符结尾的方法则相反:它们在左侧的操作元上调用,传入右侧的操作元。因此a * b 交出a.*(b),而a ::: b将交出b.:::(a)

不过,不论操作符的结合性是哪一种,它的操作元都是从左到右被求值的。因此,如果a不是一个简单的引用某个不可变值的表达式,那么a ::: b更准确地说是被当作如下的代码块:

在这个代码块中,a仍然先于b被求值,然后这个求值结果被作为操作元传入b:::方法。

这个结合性规则在相同优先级的操作符并排出现时也有相应的作用。如果方法名以“:”结尾,它们会被从右向左依次分组;否则,它们会被从左向右依次分组。例如,a ::: b ::: c 被当作a ::: (b ::: c),而a * b * c则被当作(a * b) * c

操作符优先级是Scala语言的一部分,在使用时你不需要过于担心。话虽如此,一个好的编码风格是清晰地表达出什么操作符被用在什么表达式上。也许唯一你可以真正放心让其他程序员能够不查文档就能知道的优先级规则是,乘法类的操作符(*、/%)比加法类的操作符(+、-)拥有更高的优先级。因此,尽管a + b << c在不加任何圆括号的情况下,交出你想要的结果,把表达式写成(a + b) << c带来的额外的清晰效果,也可能会减少别人用操作符表示法对你表达不满的频率,比如愤懑地大声说这是“bills !*&^%~ code!”。[8]