Метод выполнения головоломки в Scala
Сначала я объявляю класс:
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?
2 ответа
Простые правила
Есть два правила, которые определяют порядок 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)
, как и ожидалось.
op1 +++ op2 +++ op3 *** op4
эквивалентно
((op1 +++ op2) +++ (op3 *** op4))
так как вызовы метода являются левоассоциативными. Таким образом, сначала (op1 +++ op2)
оценивается, так как это первый операнд ко второму +++
, Затем второй операнд, (op3 *** op4)
оценивается. И наконец, самый внешний оператор оценивается.
То же самое будет верно для op1 + op2 + op3 * op4
в C или Java.