Scala操作符的優先級和關聯性
操作符的優先級決定了表達式的哪個部分先于其他部分被評估。舉例來說,表達式2 + 2 * 7計算得16,而不是28,因為*操作符比+操作符有更高的優先級。因此表達式的乘法部分先于加法部分被評估。當然你還可以在表達式里使用括號來厘清評估次序或覆蓋優先級。例如,如果你實際上希望上面表達式的值是28,你可以這么寫表達式:
51CTO編輯推薦:Scala編程語言專題
- (2 + 2) * 7
由于Scala沒有操作符,實際上,是以操作符的格式使用方法的一個途徑,你或許想知道操作符優先級是怎么做到的。Scala基于操作符格式里方法的第一個字符決定優先級(這個規則有一個例外,稍后再說)。比方說,如果方法名開始于*,那么就比開始于+的方法有更高的優先級。因此2 + 2 * 7將被評估為2 + (2 * 7),而a +++ b *** c(這里a,b和c是值或變量,而+++和***是方法)將被看作是a +++ (b *** c),因為***方法比+++方法有更高的優先級。
表格 5.3 操作符優先級
表格5.3以降序方式展示了根據方法第一個字符指定的優先級,同一行的字符具有同樣的優先級。表格中字符的位置越高,以這個字符開始的方法具有的優先級就越高。舉例如下:
<<方法開始于字符<,在表格5.3里的位置比+(+方法的第一個也是唯一的一個字符)要低。因此<<比+的優先級低,表達式也要在先調用了+方法之后再調用<<方法,如2 << (2 + 2)。我們可以算一下,2 + 2得4,2 << 4得32。下面給出另一個例子:
- scala> 2 << 2 + 2
- res41: Int = 32
由于第一個字符與前面的例子里一樣,因此調用的方法順序也沒有不同。首先+方法被調用,然后是<<方法。因此2 + 2得4,4 << 2得16。
- scala> 2 + 2 << 2
- res42: Int = 16
上面提到的優先級規則的一個例外,有關于以等號結束的賦值操作符:assignment operator。如果操作符以等號字符(=)結束,且操作符并非比較操作符<=,>=,==,或=,那么這個操作符的優先級與賦值符(=)相同。也就是說,它比任何其他操作符的優先級都低。例如:
與下面的相同:
- x *= y + 1
因為*=被當作賦值操作符,它的優先級低于+,盡管操作符的第一個字符是*,似乎其優先級高于+。
- x *= (y + 1)
當同樣優先級的多個操作符肩并肩地出現在表達式里,操作符的關聯性:associativity決定了操作符分組的方式。Scala里操作符的關聯性取決于它的最后一個字符。正如第3章里47頁提到的,任何以‘:’字符結尾的方法由它的右手側操作數調用,并傳入左操作數。以其他字符結尾的方法有其他的說法。它們都是被左操作數調用,并傳入右操作數。因此a * b變成a.*(b),但是a:::b變成b.:::(a)。然而,不論操作符具有什么樣的關聯性,它的操作數總是從左到右評估的。因此如果b是一個表達式而不僅僅是一個不可變值的指針的話,那么更精確的意義上說,a:::b將會當作是:
- { val x = a; b.:::(x) }
這個代碼塊中,a仍然在b之前被評估,然后評估結果被當作操作數傳給b的:::方法。
這種關聯性規則在同時使用多個具有同優先級的操作符時也會起作用。如果方法結束于:,它們就被自右向左分組;反過來,就是自左向右分組。例如,a ::: b ::: c會被當作a ::: (b ::: c)。而a * b * c被當作(a * b) * c。
操作符優先級也是Scala語言的一部分。你不用怕它。但無論如何,使用括號去厘清什么操作符作用在哪個表達式上都是好的風格。或許你唯一可以確信其他人不用查書就知道的優先級關系就是乘除法操作符(*,/和%),比加減法(+和-)的要高。因此即使a + b << c不用括號也能產生你想要的結果,寫成(a + b) << c而得到的簡潔性也可能會減少你的同事為了表示不滿在操作符注釋里寫你名字的頻率,“bills!*&^%~code!” 到目前為止,你應該能指出給出的這段代碼,Scala編譯器會調用成(bills.!*&^%~(code)).!()。
【相關閱讀】