Что означает ассоциативность слева направо?
Я запутался в определении ассоциативности слева направо и справа налево. Я также видел, как они называются левой ассоциативностью и правой ассоциативностью, и хотел бы знать, что соответствует чему.
Я знаю, что это относится к порядку, в котором операции с тем же приоритетом предварительно сформированы, например, означает ли a = x * y * z = x * (y * z) или A = (x * y) * z. Я не знаю, какой из них ассоциативный слева направо, а какой справа налево ассоциативный.
Я пробовал Googleing это, но все, что я смог найти-это таблицы, что ассоциативность различных операторов в C++. Глядя на все эти примеры, я еще больше запутался.
что же смущает меня это:
glm::vec4 transformedVector = translationMatrix * rotationMatrix * scaleMatrix * originalVector;
сначала преформирует умножение матрицы масштабирования, а затем матрицу вращения с последующим переводом. В этом примере все матрицы имеют тип glm:: mat4, а векторы имеют тип glm:: vec4. Это слева-направо или справа-налево ассоциативность? Это то же самое, что и обычное умножение, или умножение типов glm отличается?
6 ответов
вы обычно читаете слева направо. Вы обычно делаете математику слева направо. Это ассоциативность слева направо, и она наиболее распространена.
большинство людей решат
x = 23 + 34 + 45
группировка это
x = (23 + 34) + 45
это ассоциативность слева направо. Вы можете запомнить это, потому что Вы читаете и делаете математику слева направо.
математика это не имеет большого значения. В любом случае результат всегда один и тот же. Это потому что сложение ассоциативные. Сказать, что операция ассоциативна, означает, что слева направо и справа налево ассоциации-это одно и то же. Для сложения в программировании это все еще имеет значение из-за переполнения и арифметики с плавающей запятой (но не для целых чисел нормального размера на любом разумном языке), поэтому, когда у вас есть ошибка 2 AM с большими числами и легкомысленное использованиеa+b
и b+a
, помните, в каком порядке произошло добавление.
в вашей пример:
glm::vec4 transformedVector = translationMatrix * rotationMatrix * scaleMatrix * originalVector
вы принципиально сначала проглотите с правой стороны, так как именно там находится то, на что вы действуете. Однако в C++, *
обычно слева направо ассоциативность и это невозможно переопределить. glm может обрабатывать это несколькими способами: он может создавать кэш вещей для умножения, ожидая прибытия окончательного вектора затем сделайте умножение справа налево. Это также может (более вероятно) используйте теорему алгебры о том, что умножение матрицы полностью ассоциативно, и просто умножайте слева направо, а затем заверьте читателя в документации, что это то же самое, что думать об этом справа налево. Однако,вам нужно понять реализацию потому что, как обсуждалось ранее имеет значение, каким образом реализация выбирает умножение чисел с плавающей запятой вместе.
для полноты картины, рассмотрим вычитание. Что a - b - c
? Вот это действительно тут имеет значение, является ли он левым или правым ассоциативным. Конечно, в математике мы определяем его как b (a - b) - c
, но какой-то странный язык программирования может предпочесть вычитание, чтобы быть правильным ассоциативным, и взять a - b - c
всегда означает a - (b - c)
. Этот чужой язык должен иметь страницу документации, указывающую, что -
прав-ассоциативно, потому что оно часть спецификации деятельности, не что-то вы можете сказать просто от смотреть оператора использовать.
вы можете видеть это из следующих слов:
когда мы объединяем операторы для формирования выражений, порядок, в котором операторы, которые будут применяться, могут быть неочевидными. Например, a + b + c можно интерпретировать как ((a + b) + c) или как (a + (b + c)). Мы говорим, что + лево-ассоциативным, если операнды группируются слева на прямо как в ((a + b) + c). Мы говорим, что это правильно-ассоциативно, если это группирует операнды в противоположном направлении, как в (a + (b + с.))
А. В. АХО & Дж. д. Ульман 1977, стр. 47
простой и не многабукаф. ответ я нашел:
в большинстве языков программирования операторы сложения, вычитания, умножения и деления являются левую ассоциативность, в то время как операторы присваивания, условные и экспоненциальные являются правую ассоциативность.
a = (x * y) * z
-слева-направо и a = x * (y * z)
справа-налево.
*
оператора. Вопрос здесь заключается в смысл умножения матрицы в терминах геометрических преобразований, а не математической ассоциативности.оператор infix (или более общий тип выражения, который имеет незамкнутые левые и правые под-выражения) является левым ассоциативным, если во вложенном использовании этого оператора (типа выражения) без явных круглых скобок неявные круглые скобки помещаются слева. С *
лево-ассоциативным в C++, a*b*c
означает (a*b)*c
. В случае более глубокого вложенности кластер неявных скобок возникает на левом конце:(((a*b)*c)*d)*e
.
эквивалентно это означает синтаксическое производство правило для этого оператора является леворекурсивной (т. е. левая часть выражения имеет одну и ту же синтаксическую категорию, как один из этого правила является производство по делам, так что одно и то же правило (тот же оператор) может быть использован непосредственно в форме, что суб-выражение; подвыражения в другой конец имеет более строгие синтаксические категории, и, используя один и тот же оператор будет требовать явного скобках). В C++ одна продукция для мультипликативное-выражение (раздел 5.6 стандарта) читает mutliplicative-выражение *
pm-выражение С мультипликативное-выражение слева.
следовательно, во вложенном использовании без явных круглых скобок самый левый оператор принимает своих ближайших соседей как операнды, в то время как другие экземпляры принимают за левый операнд (результат) выражения, сформированного всем слева от них.
я признаю, я немного подтолкнул это (слишком далеко). Моя точка зрения в этом нигде выше не встречается слово "право", и нет никакого движения; ассоциативность-это синтаксическая и, следовательно, статическая материя. Важно здесь неявные круглые скобки идут, а не в каком порядке их пишут (на самом деле их вообще нет, иначе они были бы явными). Конечно, для правой ассоциативности просто замените каждое "левое" на "правое" выше.
в заключение, я не вижу веской причины, почему следует называть эту ассоциативность слева направо (или группировка), но факт в том, что люди делают (даже стандарт делает, хотя он совершенно избыточен, учитывая, что также даны явные правила синтаксиса).
путаница происходит от объясняя это, как это часто делают, говоря, что (в отсутствие явных скобок) операторы выполняются слева направо (соответственно справа налево по правой ассоциативностью). Это вводит в заблуждение, поскольку путает синтаксис с семантикой (выполнение), а также допустимо только для операции с оценкой снизу вверх (все операнды оцениваются до оператора). Для операторов со специальными правилами оценки это просто неправильно. Для операторов &&
(и) и ||
(или) семантика заключается в том, чтобы сначала оценить левый операнд, а затем сам оператор (а именно решить, будет ли левый или правый операнд производить результат) следовал возможно по оценке правого операнда. Эта оценка слева направо полностью не зависит от ассоциативность: операторы оказываются левоассоциативными, вероятно, потому, что все двоичные операторы без назначения, но (c1 && c2) && c3
(с избыточными скобками, где они уже неявно будут) имеет эквивалентное выполнение c1 && (c2 && c3)
(а именно выполнить условия слева направо, пока не вернется false
и верните это, или если никто не вернется true
), и я не могу представить разумный компилятор, генерирующий другой код для двух случаев. На самом деле я нахожу правильную группировку больше наводит на мысль о том, как оценивается выражение, но это действительно не имеет значения; то же самое касается or
.
это еще более ясно для условного (троичного) оператора ? ... :
. Здесь применима ассоциативность, потому что с обеих сторон есть открытые под-выражения (см. мое начальное предложение); средний операнд заключен в ?
и :
и никогда требует дополнительных скобок. Действительно, этот оператор объявлен право-ассоциативный, что означает, что c1 ? x : c2 ? y : z
следует читать как c1 ? x : (c2 ? y : z)
, а не (c1 ? x : c2) ? y : z
(неявные скобки справа). Однако с неявными круглыми скобками два тернарных оператора выполняются слева направо; объяснение заключается в том, что семантика тернарного оператора сначала не оценивает все под-выражения.
вернемся к примеру из вашего вопроса, лево-ассоциативность (или группировка слева направо) означает, что ваш матрично-векторный продукт анализируется как ((M1*M2)*M3)*v
. Хотя математически эквивалентно, практически невозможно, чтобы это было выполнено как M1*(M2*(M3*v))
, хотя это более эффективно. Причина в том, что умножение с плавающей запятой не является истинно ассоциативным (только приблизительно), и поэтому умножение матрицы с плавающей запятой; поэтому компилятор не может преобразовать одно выражение в другое. Обратите внимание, что в ((M1*M2)*M3)*v
нельзя сказать, какая из матриц сначала применяется к вектору, потому что ни один из них не является: матрицей композитные линейные карты вычисляются первыми, и это матрица применяется к вектору. Результат будет примерно равен результату M1*(M2*(M3*v))
, в котором M3
, то M2
и наконец M1
. Но если вы хотите, чтобы что-то произошло, вы должны написать эти скобки.
слева направо ассоциативность оператора означает правую сторону от оператора не должно быть никаких операторов высших precedece(приоритет), но это может иметь такой же приоритет. Если есть какой-либо оператор более высокого приоритета на правой стороне нашего оператора, тогда мы должны решить его в первую очередь.пример:
x = 2 + 3 * 3;
здесь, для оператора + (слева направо ассоциативность), правая сторона содержит оператор *, который имеет более высокий приоритет, чем оператор+, поэтому мы имеем сначала решить.
x = 2 + 9;
x = 11;