Введение

Свойство, определяющее, как группируются операторы равного приоритета – математическая концепция ассоциативности.

В теории языков программирования ассоциативность оператора – это свойство, определяющее, как группируются операторы с одинаковым приоритетом при отсутствии скобок. Если перед операндом и после него следуют операторы (например, ^ 3 ^), и эти операторы имеют одинаковый приоритет, то операнд может использоваться в качестве аргумента для двух различных операций (то есть двух операций, обозначенных этими операторами). Выбор операций, к которым применяется операнд, определяется ассоциативностью операторов. Операторы могут быть ассоциативными (то есть операции могут быть сгруппированы произвольно), левоассоциативными (то есть операции группируются слева), правоассоциативными (то есть операции группируются справа) или неассоциативными (то есть операции нельзя объединять в цепочку, часто из-за несовместимости типа результата с типами аргументов). Ассоциативность и приоритет оператора являются частью определения языка программирования; разные языки программирования могут иметь разную ассоциативность и приоритет для одного и того же типа оператора. Рассмотрим выражение a ~ b ~ c. Если оператор ~ имеет левую ассоциативность, это выражение будет интерпретироваться как (a ~ b) ~ c. Если оператор имеет правую ассоциативность, выражение будет интерпретироваться как a ~ (b ~ c). Если оператор неассоциативен, выражение может быть синтаксической ошибкой или иметь специальное значение. Некоторые математические операторы обладают внутренней ассоциативностью. Например, вычитание и деление, используемые в общепринятой математической записи, по своей сути левоассоциативны. С другой стороны, сложение и умножение являются как лево-, так и правоассоциативными (например, (a * b) * c = a * (b * c)). Многие руководства по языкам программирования содержат таблицу приоритетов и ассоциативности операторов; см., например, таблицу для C и C++. Концепция ассоциативности обозначений, описанная здесь, связана с математической ассоциативностью, но отличается от неё. Операция, которая математически ассоциативна, по определению не требует ассоциативности обозначений. (Например, сложение обладает ассоциативным свойством, поэтому оно не должно быть ни лево-, ни правоассоциативным.) Операция, которая не является математически ассоциативной, однако, должна быть ассоциативной в обозначениях – лево-, право- или неассоциативной. (Например, вычитание не обладает ассоциативным свойством, поэтому оно должно иметь ассоциативность обозначений.)

Примеры

Ассоциативность необходима только тогда, когда операторы в выражении имеют одинаковый приоритет. Обычно + и - имеют одинаковый приоритет. Рассмотрим выражение 7 - 4 + 2. Результат может быть либо (7 - 4) + 2 = 5, либо 7 - (4 + 2) = 1. Первый результат соответствует случаю, когда + и - левоассоциативны, второй – когда + и - правоассоциативны. Чтобы отразить обычное использование, операторы сложения, вычитания, умножения и деления обычно левоассоциативны, в то время как для оператора возведения в степень (если он присутствует) нет единого мнения. Любые операторы присваивания, как правило, правоассоциативны. Чтобы избежать ситуаций, когда операнды будут связаны с двумя операторами или не будут связаны ни с одним оператором, операторы с одинаковым приоритетом должны иметь одинаковую ассоциативность.

Право-ассоциативность операторов присвоения

Во многих императивных языках программирования оператор присваивания определяется как правоассоциативный, а присваивание определяется как выражение (которое вычисляется в значение), а не просто оператор. Это позволяет использовать цепочки присваиваний, используя значение одного выражения присваивания в качестве правого операнда следующего выражения присваивания. В C присваивание `a = b` является выражением, которое вычисляется в то же значение, что и выражение `b`, преобразованное к типу `a`, с побочным эффектом сохранения R-значения `b` в L-значение `a`. Поэтому выражение `a = (b = c)` может быть интерпретировано как `b = c; a = b;`. Альтернативное выражение `(a = b) = c` вызывает ошибку, потому что `a = b` не является L-значным выражением, то есть оно имеет R-значение, но не L-значение, куда можно сохранить R-значение `c`. Правоассоциативность оператора `=` позволяет интерпретировать такие выражения, как `a = b = c`, как `a = (b = c)`. В C++ присваивание `a = b` является выражением, которое вычисляется в то же значение, что и выражение `a`, с побочным эффектом сохранения R-значения `b` в L-значение `a`. Поэтому выражение `a = (b = c)` по-прежнему может быть интерпретировано как `b = c; a = b;`. И альтернативное выражение `(a = b) = c` может быть интерпретировано как `a = b; a = c;` вместо вызова ошибки. Правоассоциативность оператора `=` позволяет интерпретировать выражения, такие как `a = b = c`, как `a = (b = c)`.

Неассоциативные операторы

Неассоциативные операторы — это операторы, для которых не определено поведение при последовательном использовании в выражении. В Prolog инфиксный оператор `:` не является ассоциативным, поскольку конструкции вроде "a : b : c" приводят к синтаксическим ошибкам. Другая возможность заключается в том, что последовательности определенных операторов интерпретируются иным способом, который нельзя выразить через ассоциативность. Обычно это означает, что синтаксически для последовательностей этих операций существует специальное правило, а семантически поведение отличается. Хорошим примером служит Python, в котором есть несколько подобных конструкций. Поскольку присваивания являются операторами, а не операциями, оператор присваивания не имеет значения и не является ассоциативным. Цепное присваивание реализуется посредством грамматического правила для последовательностей присваиваний вида `a = b = c`, которые затем выполняются слева направо. Кроме того, комбинации присваивания и расширенного присваивания, такие как `a = b += c`, недопустимы в Python, хотя они допустимы в C. Другой пример — операторы сравнения, такие как >, == и <=. Цепное сравнение, например `a < b < c`, интерпретируется как `(a < b) и (b < c)`, а не эквивалентно `(a < b) < c` или `a < (b < c)`.