Метод выполнения головоломки в 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.

Другие вопросы по тегам