首先我宣布一个班级:
class Op(var x : Int) {
def +++(op: Op) = {
println(this.x + " +++ " + op.x)
this.x += op.x
this
}
def ***(op: Op) = {
println(this.x + " *** " + op.x)
this.x *= op.x
this
}
}
现在我在REPL中执行表达式:
op1 +++ op2 +++ op3 *** op4
并输出
但为什么方法***
不是先行? ***
的优先级不高于+++
吗? Java和C怎么样?它和Scala一样吗?
答案 0 :(得分:13)
op1 +++ op2 +++ op3 *** op4
相当于
((op1 +++ op2) +++ (op3 *** op4))
因为方法调用是左关联的。因此,首先评估(op1 +++ op2)
,因为它是第二个+++
的第一个操作数。然后评估第二个操作数(op3 *** op4)
。最后,评估最外层的运算符。
对于C或Java中的op1 + op2 + op3 * op4
也是如此。
答案 1 :(得分:5)
有两个规则决定op1 +++ op2 +++ op3 *** op4
表达式评估的顺序:
首先,因为以*开头的运算符优先于以+开头的运算符,所以表达式转换为:
op1 +++ op2 +++ (op3 *** op4)
其次,因为有多个相同优先级的运算符并排出现(op1 +++ op2 +++ ...),所以它们从左到右分组:
(op1 +++ op2) +++ (op3 *** op4)
考虑以下表达式:
op1 +++ op2 +++ op3 +++ op4 *** op5
遵循相同的两个简单规则,它将被评估为:
((op1 +++ op2) +++ op3) +++ (op4 *** op5)
或者,我们将相同的两个规则应用于op1 +++ op2 +++ op3 +++ op4 *** op5 *** op6
:
以*开头的运算符优先于以+:
开头的运算符op1 +++ op2 +++ op3 +++ (op4 *** op5 *** op6)
然后从左到右分组具有相同优先级的运算符:
((op1 +++ op2) +++ op3) +++ ((op4 *** op5) *** op6)
如果+++
和***
方法没有任何副作用,则分组具有完美的数学意义。考虑:
op1 +++ op2 +++ op1 *** op2
直观地,表达式应该返回一个持有5的对象。但是,由于+++
和***
方法在原始代码示例中产生的不幸副作用(两者都修改了存储在对象中的值)表达式将导致一个对象保持12而不是预期的5.
这就是为什么在构造这样的表达式时最好完全依赖不可变对象:
case class Op ( x: Int) {
def +++(that: Op) = {
println(this.x + " +++ " + that.x)
Op(this.x+that.x)
}
def ***(that: Op) = {
println(this.x + " *** " + that.x)
Op(this.x * that.x)
}
}
Op(1) +++ Op(2) +++ Op(1) *** Op(2)
表达式将按预期生成Op(5)
。