Метод выполнения головоломки в Scala - PullRequest
5 голосов
/ 25 ноября 2011

Сначала я объявляю класс:

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

и выводит

enter image description here

Но почему метод *** не идет первым? Разве приоритет *** не выше +++? А как насчет Java и C? Это так же, как в Scala?

Ответы [ 2 ]

13 голосов
/ 25 ноября 2011
op1 +++ op2 +++ op3 *** op4

эквивалентно

((op1 +++ op2) +++ (op3 *** op4))

так как вызовы методов левоассоциативны. Таким образом, первый (op1 +++ op2) оценивается, поскольку это первый операнд второго +++. Затем оценивается второй операнд (op3 *** op4). И, наконец, оценивается самый внешний оператор.

То же самое будет верно для op1 + op2 + op3 * op4 в C или Java.

5 голосов
/ 26 ноября 2011

Простые правила

Существует два правила, определяющих порядок 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)
  }  
}

The Op(1) +++ Op(2) +++ Op(1) *** Op(2) выражение приведет к Op(5), как и ожидалось.

...