Подтвердить что ты не робот

Что означает ассоциативность слева направо?

Я запутался в определении ассоциативности слева направо и справа налево. Я также видел, что они называются левой ассоциативностью и правой ассоциативностью и хотели бы знать, что соответствует.

Я знаю, что он связан с порядком, в котором выполняются операции с одинаковым приоритетом, как в случае, если a = x * y * z означает a = x * (y * z) или = (x * y) * z, Я не знаю, какая из них является ассоциацией слева направо и которая является ассоциативной справа налево.

Я попробовал Google, но все, что мне удалось найти, это таблицы того, что ассоциативность разных операторов в С++. Взгляд на все примеры только что смутил меня.

Что еще меня смущает, так это то, что:

glm::vec4 transformedVector = translationMatrix * rotationMatrix * scaleMatrix * originalVector;

сначала формирует умножение матрицы масштабирования, за которым следует матрица вращения, за которой следует перевод. В этом примере все матрицы имеют тип glm:: mat4, а векторы имеют тип glm:: vec4. Является ли это ассоциацией слева направо или справа налево? Это то же самое, что и нормальное умножение или умножение типов glm?

4b9b3361

Ответ 1

Обычно вы читаете слева направо. Вы обычно делаете математику слева направо. Это имеет право на правую ассоциативность, и это наиболее распространено.

Большинство людей решат

x = 23 + 34 + 45

группируя его

x = (23 + 34) + 45

это ассоциация слева направо. Вы можете запомнить это, потому что вы читаете и делаете математику слева направо.

Для добавления в математику это не слишком важно. Вы всегда получаете одинаковый результат в любом случае. Это связано с тем, что добавление ассоциативно. Говорить, что операция является ассоциативной, означает, что объединение слева направо и справа налево - одно и то же. Для добавления в программирование все еще имеет значение из-за переполнения и арифметики с плавающей запятой (но не будет для целых чисел нормального размера на любом разумном языке), поэтому, когда у вас есть ошибка 2 AM с большими числами и легкомысленное использование a+b и b+a, помните, в каком порядке произошло добавление.

В вашем примере:

glm::vec4 transformedVector = translationMatrix * rotationMatrix * scaleMatrix * originalVector

Вы концептуально перебираете с правой стороны, так как именно там вы действуете. Однако в С++ * обычно ассоциативно слева и справа, и это невозможно переопределить. glm может справиться с этим несколькими способами: он может создать кеш файлы для умножения ожиданий конечного вектора, а затем выполнить умножение справа налево. Он также может (более вероятно) использовать теорему алгебры о том, что матричное умножение полностью ассоциативно и просто умножать слева направо, а затем заверить читателя в документации, что он так же, как думать о нем как правее слева. Тем не менее, вам нужно понять реализацию, поскольку, как обсуждалось ранее важно, каким образом реализация решает умножить числа с плавающей точкой вместе.

Для полноты рассмотрим вычитание. Что такое a - b - c? Здесь действительно имеет значение, является ли он левым или правым ассоциативным. Конечно, в математике мы определяем его на b (a - b) - c, но какой-то странный язык программирования может предпочесть, что вычитание является правильным ассоциативным, и принимать a - b - c всегда означает a - (b - c). Этот чужой язык лучше иметь страницу документации, указывающую, что - является право-ассоциативным, потому что он является частью спецификации операции, а не тем, что вы можете сказать просто, глядя на использование оператора.

Ответ 2

Это можно увидеть из следующих слов:

When we combine operators to form expressions, the order in which
the operators are to be applied may not be obvious. For example,
a + b + c can be interpreted as ((a + b) + c) or as (a + (b + c)).
We say that + is left-associative if operands are grouped left to
right as in ((a + b) + c). We say it is right-associative if it
groups operands in the opposite direction, as in (a + (b + c)).

                                A.V. Aho & J.D. Ullman 1977, p. 47

И лево-ассоциативный - это то же самое значение , связывающего слева направо, также то же значение ассоциация слева направо.

Ответ 3

a = (x * y) * z слева направо и a = x * (y * z) справа налево.

Матрица матриц glm связывает слева направо, поскольку она перегружает оператор *. Здесь речь идет о значении значения матричных умножений в терминах геометрических преобразований, а не математической ассоциативности.

Ответ 4

Инфиксный оператор (или более общий тип выражения, который имеет незакрытые левые и правые подвыражения) остается ассоциативным, если во вложенном использовании этого оператора (тип выражения) без явных скобок, неявные скобки располагаются слева. Так как * лево-ассоциативный в С++, a*b*c означает (a*b)*c. В случае более глубокого вложения в левом конце появляется кластер неявных скобок: (((a*b)*c)*d)*e.

Эквивалентно это означает, что синтаксическое правило производства для этого оператора леворекурсивно (это означает, что левое подвыражение имеет ту же синтаксическую категорию, что и это правило, для производства, так что одно и то же правило (тот же оператор) может быть используется непосредственно для формирования этого подвыражения, подвыражение на другом конце имеет более ограничительную синтаксическую категорию, и использование того же оператора потребует явных скобок). В С++ одно произведение для мультипликативного выражения (раздел 5.6 в стандарте) читает выражение mutliplicative-expression * pm-выражение с мультипликативным выражением слева.

Следовательно, при вложенном использовании без явных скобок самый левый оператор принимает своих непосредственных соседей в качестве операндов, в то время как другие экземпляры принимают в качестве левого операнда результат (результат) выражения, образованного всем слева.

Я признаю, что я немного подтолкнул это (слишком далеко). Моя точка в том, что нигде выше не происходит слово "право", и нет никакого движения; ассоциативность является синтаксической и, следовательно, статической материей. Важно, чтобы неявные круглые скобки проходили не в том порядке, в котором они записываются (на самом деле это совсем не так, иначе они будут явными). Конечно, для правильной ассоциативности просто замените каждый "левый" на "правый" выше.

В заключение я не вижу никакой веской причины, почему нужно называть эту ассоциативную ассоциацию слева направо (или группировку), но факт состоит в том, что люди делают (даже стандартный, хотя и совершенно избыточный, учитывая, что явный синтаксис правила также даются).

Путаница объясняется этим, как это часто делается, утверждая, что (в отсутствие явных скобок) операторы выполняются слева направо (соответственно справа налево для право-ассоциативных операторов). Это вводит в заблуждение, потому что оно сбивает синтаксис с семантикой (выполнение), а также действует только для операций с оценкой снизу вверх (все операнды оцениваются до того, как оператор). Для операторов со специальными правилами оценки это просто неправильно. Для операторов && (и) и || (или) семантика состоит в том, чтобы сначала оценить левый операнд, а затем сам оператор (а именно решить, будет ли левый или правый операнд выдавать результат), за которым следует, возможно, оценка правый операнд. Эта оценка слева направо полностью не зависит от ассоциативности: операторы оказываются лево-ассоциативными, вероятно, потому, что все двоичные операторы без присвоения, но (c1 && c2) && c3 (с избыточными скобками, где они уже были бы неявно) имеет эквивалентное выполнение c1 && (c2 && c3) (а именно выполнить условия слева направо, пока один не вернет false и не вернет это, или если ни один из них не вернет true)), и я не могу представить разумный компилятор, генерирующий другой код для этих двух случаев. На самом деле я нахожу правильную группировку, более наводящую на мысль о том, как оценивается выражение, но это действительно не имеет значения; то же самое для or.

Это еще более понятно для условного (тройного) оператора ? ... :. Здесь ассоциативность применяется, потому что есть открытые подвыражения с обеих сторон (см. Мое вступительное предложение); средний операнд заключен в ? и : и никогда не требует дополнительных скобок. Действительно, этот оператор объявлен right -associative, что означает, что 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. Но если вы хотите, чтобы все происходило так, вам нужно написать эти круглые скобки.

Ответ 5

Самый простой и самый не-tl; dr ответ Я нашел:

В большинстве языков программирования операции сложения, вычитания, умножения и деления являются лево-ассоциативными, а операторы присваивания, условного выражения и возведения в степень являются правильными ассоциативными.

благодаря: http://www.computerhope.com/jargon/a/assooper.htm

Ответ 6

Слева к правой ассоциативности оператора означает правую часть оператора не должен иметь оператора более высокого приоритета (приоритет), но он может иметь одинаковую приоритетность. Если есть какой-либо оператор с более высоким приоритетом на правой стороне нашего оператора, тогда мы должны решить его сначала. Пример:

x = 2 + 3 * 3;

Здесь для оператора + (слева-справа ассоциативность) правая часть содержит оператор *, который имеет более высокий приоритет, чем + оператор, поэтому мы имеем для его решения в первую очередь.

x = 2 + 9;
x = 11;

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь