В теории языков программирования ассоциативность оператора — это свойство, которое определяет, как операторы с одинаковым приоритетом группируются при отсутствии скобок . Если операнду предшествуют и следуют операторы (например, ), и эти операторы имеют одинаковый приоритет, то операнд может использоваться в качестве входных данных для двух различных операций (т. е. двух операций, указанных двумя операторами). Выбор операций, к которым следует применять операнд, определяется ассоциативностью операторов . Операторы могут быть ассоциативными (то есть операции могут быть сгруппированы произвольно), левоассоциативными (то есть операции сгруппированы слева), правоассоциативными (то есть операции сгруппированы справа) или неассоциативными (то есть операции не могут быть объединены в цепочку, часто потому, что тип вывода несовместим с типами ввода). Ассоциативность и приоритет оператора являются частью определения языка программирования; разные языки программирования могут иметь разную ассоциативность и приоритет для одного и того же типа оператора.^ 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
. Первый результат соответствует случаю, когда +
и -
являются левоассоциативными, последний — когда +
и -
являются правоассоциативными.
Чтобы отразить нормальное использование, операторы сложения , вычитания , умножения и деления обычно являются левоассоциативными, [1] [2] [3] тогда как для оператора возведения в степень (если он присутствует) [4] [ нужен лучший источник ] общего соглашения нет. Любые операторы присваивания обычно являются правоассоциативными. Чтобы предотвратить случаи, когда операнды будут связаны с двумя операторами или ни с одним оператором вообще, операторы с одинаковым приоритетом должны иметь одинаковую ассоциативность.
Рассмотрим выражение 5^4^3^2
, в котором ^
принимается правоассоциативный оператор возведения в степень. Анализатор, считывающий токены слева направо, применит правило ассоциативности к ветви, ввиду правоассоциативности ^
, следующим образом:
5
зачитан.^
считывается. Узел: " 5^
".4
прочитан. Узел: " 5^4
".^
считывается, запуская правило правой ассоциативности. Ассоциативность определяет узел: " 5^(4^
".3
прочитан. Узел: " 5^(4^3
".^
считывается, вызывая повторное применение правила правой ассоциативности. Узел " 5^(4^(3^
".2
прочитан. Узел " 5^(4^(3^2
".5^(4^(3^2))
".Затем это можно оценить в глубину, начиная с верхнего узла (первого ^
):
^
выражения через второе и третье.^
.^
.Левоассоциативная оценка привела бы к дереву синтаксического анализа ((5^4)^3)^2
и совершенно другому результату (625 3 ) 2 = 244,140,625 2 ≈5,960 4645 × 10 16 .
Во многих императивных языках программирования оператор присваивания определяется как правоассоциативный, а присваивание определяется как выражение (которое вычисляется как значение), а не просто оператор. Это позволяет выполнять цепочку присваиваний , используя значение одного выражения присваивания в качестве правого операнда следующего выражения присваивания.
В языке C присваивание a = b
— это выражение, результатом вычисления которого является то же значение, что и у выражения, b
преобразованного в тип a
, с побочным эффектом сохранения R-значения в b
L -значении . a
[ 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 , в котором есть несколько таких конструкций. [5] Поскольку присваивания являются операторами, а не операциями, оператор присваивания не имеет значения и не является ассоциативным. Цепочечное присваивание вместо этого реализуется с помощью грамматического правила для последовательностей присваиваний a = b = c
, которые затем присваиваются слева направо. Кроме того, комбинации присваивания и расширенного присваивания, как, например, a = b += c
не являются допустимыми в Python, хотя они допустимы в C. Другим примером являются операторы сравнения, такие как >
, ==
и <=
. Цепочечное сравнение как a < b < c
интерпретируется как (a < b) and (b < c)
, не эквивалентно ни (a < b) < c
или a < (b < c)
. [6]
a = b
является выражением, но a = b;
является утверждением.