What does left-to-right associativity mean?

后端 未结 6 684
生来不讨喜
生来不讨喜 2020-12-29 21:23

I am confused about the definition of left-to-right and right-to-left associativity. I have also seen them called left associativity and right associativity and would like t

相关标签:
6条回答
  • 2020-12-29 22:07

    Left to right associativity of a operator means right side of operator should not have any operator of higher precedece(priority), but it can be of same priority. If there is any operator of higher priority on right side of our operator, then we have to solve it first.example:

    x = 2 + 3 * 3;
    

    Here, for + operator(left to right associativity), right side contains a operator * , which is of higher priority than + operator, so we have to solve it first.

    x = 2 + 9;
    x = 11;
    

    0 讨论(0)
  • 2020-12-29 22:09

    Simplest and most non-tl;dr answer I found:

    In most programming languages, the addition, subtraction, multiplication, and division operators are left-associative, while the assignment, conditional, and exponentiation operators are right associative.

    thanks to: http://www.computerhope.com/jargon/a/assooper.htm

    0 讨论(0)
  • 2020-12-29 22:10

    a = (x * y) * z is left-to-right and a = x * (y * z) is right-to-left.

    glm's matrix multiplcation associates left-to-right because it overloads the * operator. The issue here is about the meaning of the matrix multiplications in terms of geometrical transformations, rather than the mathematical associativity.

    0 讨论(0)
  • 2020-12-29 22:16

    You can see that from the following words:

    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

    0 讨论(0)
  • 2020-12-29 22:21

    You normally read left to right. You normally do math left to right. This is left to right associativity and it is most common.

    Most people will solve

    x = 23 + 34 + 45
    

    by grouping it

    x = (23 + 34) + 45
    

    this is left-to-right associativity. You can remember it because you read and do math left to right.

    For addition in mathematics it does't matter too much. You always get the same result either way. This is because addition is associative. To say an operation is associative means left to right and right to left association are the same thing. For addition in programming it still matters because of overflows and floating point arithmetic (but won't for normal-sized integers in any reasonable language), so when you have a 2 AM bug with large numbers and flippant use of a+b and b+a, remember what order the addition happened in.

    In your example:

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

    You conceptually chomp through from the right-side first, since that is where the thing you are acting on is. However in C++, * is normally left-to-right associative and it is not possible to override this. glm may handle this in a number of ways: it may build up a cache of things to multiply waiting for the final vector to arrive then do right to left multiplication. It may also (more likely) use the theorem of algebra that matrix multiplication is fully associative, and just multiply out left-to-right, then assure the reader in the documentation that it's the same as thinking of it as right to left. However, you need to understand the implementation because as previously discussed it matters which way the implementation chooses to multiplying floating point numbers together.

    For completeness, consider subtraction. What is a - b - c? Here it really does matter whether it is left or right associative. Of course in math we define it to b (a - b) - c, but some strange programming language might prefer subtraction to be right associative, and take a - b - c to always mean a - (b - c). This alien language had better have a documentation page specifying that - is right-associative, because it is part of the operation specification, not something you can tell simply from looking at the operator's use.

    0 讨论(0)
  • 2020-12-29 22:21

    An infix operator (or more generally expression type that has unclosed left and right sub-expressions) is left associative if in nested use of this operator (expression type) without explicit parentheses, the implicit parentheses are placed at the left. Since * is left-associative in C++, a*b*c means (a*b)*c. In case of deeper nesting, a cluster of implicit parentheses occurs on the left end: (((a*b)*c)*d)*e.

    Equivalently this means the syntactic production rule for this operator is left-recursive (meaning the left sub-expression has the same syntactic category as the one this rule is a production for, so that the same rule (same operator) may be used directly to form that sub-expression; the sub-expression at the other end has a more restrictive syntactic category, and using the same operator there would require explicit parentheses). In C++, one production for multiplicative-expression (section 5.6 in the Standard) reads mutliplicative-expression * pm-expression, with multiplicative-expression on the left.

    Consequently, in a nested use without explicit parentheses, the leftmost operator takes its immediate neighbours as operands, while the other instances take as left operand the (result of) the expression formed by everything to their left.

    I admit, I've been pushing this a bit (too far). My point in that nowhere above does the word "right" occur, nor is there any movement involved; associativity is a syntactic and therefore static matter. It is important where the implicit parentheses go, not in which order one writes them (in fact one does not at all, or else they would be explicit). Of course, for right-associativity, just replace each "left" by "right" above.

    In conclusion, I can see no valid reason why one should call this left-to-right associativity (or grouping), but the fact is that people do (even the Standard does, though it is perfectly redundant given that explicit syntax rules are also given).

    Confusion comes from explaining this, as is often done, by saying that (in absence of explicit parentheses) the operators are performed from left to right (respectively from right-to-left for right-associative operators). This is misleading because it confuses syntax with semantics (execution), and also is only valid for operations with bottom-up evaluation (all operands are evaluated before the operator is). For operators with special evaluation rules it is simply wrong. For the operators && (and) and || (or) the semantics is to evaluate the left operand first then the operator itself (namely decide whether the left or the right operand will produce the result) followed possibly by the evaluation of the right operand. This left-to-right evaluation is totally independent of the associativity: the operators happen to be left-associative, probably because all non-assignment binary operators are, but (c1 && c2) && c3 (with redundant parentheses where they would already implicitly be) has an equivalent execution to c1 && (c2 && c3) (namely execute the conditions from left to right until one returns false and return that, or if none does return true), and I cannot imagine a reasonable compiler generating different code for the two cases. Actually I find the right-grouping more suggestive of how the expression is evaluated, but it really makes no difference; the same goes for or.

    This is even more clear for the conditional (ternary) operator ? ... :. Here associativity applies, because there are open sub-expressions at both sides (cf. my opening sentence); the middle operand is enclosed in ? and : and never requires additional parentheses. Indeed this operator is declared to be right-associative, which means that c1 ? x : c2 ? y : z should be read as c1 ? x : (c2 ? y : z) rather than as (c1 ? x : c2) ? y : z (the implicit parentheses are to the right). However, with the implicit parentheses the two ternary operators are executed from left to right; the explanation is that the semantics of the ternary operator does not evaluate all sub-expressions first.


    Back to the example from your question, left-associativity (or grouping left-to-right) means that your matrix-vector product is parsed as ((M1*M2)*M3)*v. Although mathematically equivalent, it is virtually impossible that this gets executed as M1*(M2*(M3*v)), even though that is more efficient. The reason is that floating-point multiplication is not truly associative (just approximately), nor is therefore floating-point matrix multiplication; the compiler therefore cannot transform one expression into the other. Note that in ((M1*M2)*M3)*v one cannot say which of the matrices gets applied to a vector first, because none of them is: the matrix of the composite linear maps gets computed first, and the that matrix gets applied to the vector. The result will be approximately equal to that of M1*(M2*(M3*v)) in which M3 is applied, then M2 and finally M1. But if you want things to happen like that, you have to write those parentheses.

    0 讨论(0)
提交回复
热议问题