========================== Operators and precedence ========================== .. contents:: General concepts ================ Precedence ---------- It is customary in mathematics to omit parentheses to simplify complex expressions that use binary operators. For example we often write “:math:3+2+1” instead of the more complex “:math:(3+2)+1”. However as soon as we use different types of operators, the question immediately arises: is “:math:1 + 2\times 3” equal to “:math:(1+2)\times 3” or “:math:1+(2\times 3)”? In mathematics at least, we use *precedence rules* to disambiguate: we say that “multiplication has a *higher precedence* than addition”, so the sign :math:\times binds to its operands “tighter” than the sign :math:+. Since we have multiple operators in arithmetic at least, we use a *precedence table* to determine which operators bind tighter than others: ======================== =================================== Arithmetic operator Precedence (higher binds tighter) ======================== =================================== :math:\times \div 4 :math:+ - 3 :math:= < > \leq \geq 2 :math:\Leftrightarrow 1 ======================== =================================== Thanks to this table, we know that “:math:1 < 2 + 3” is a short way to write “:math:1 < (2+3)” and not “:math:(1<2)+3”, because :math:+ has a higher precedence than :math:<. Grouping order, fixity in math ------------------------------ When we use the *same* operator two or more times, another question arises: should we group “:math:1+2+3” as “:math:(1+2)+3” or as “:math:1+(2+3)”? For the :math:+ operator, this does not matter much, because addition is associative: we get the same value in either case. However, the situation is different with :math:-: we get different values for “:math:1-2-3” if we group like this: “:math:(1-2)-3” (equals -4) or like this: “:math:1-(2-3)” (equals 2). In math there are a couple of well known operators that are not associative: :math:-, :math:\div and exponentiation (:math:(x^y)^z \neq x^{(y^z)}). For this situation, in mathematics we nearly always choose for *left fixity*: when we use the same operator two or more times in a row, we start grouping from the left. This word “fixity” means “grouping order”. Predecence and associativity in programming =========================================== Like in math, nearly all programming languages have operator precedence rules that make it possible to omit parentheses in many cases. Most often, all operators in a programming language that represent a mathematical operation have a similar precedence as in math. However, there are also other programming operators than in math, so for every programming language you will need to have a look at its *operator precedence table*. For example, a part of the Java operator precedence table looks like this: ========== ============= Operator Precedence ========== ============= * / % 3 + - 4 < > 6 == 7 ========== ============= Note the precedence level is sometimes represented, like in the table above, by a number where a lower number means a higher precedence. For example priority “1” may mean the operator binds tighter than all other operators with a larger priority number. As a rule of thumb, read the description of the table! Also, usually the highest precedence operators are listed first. In addition to precedence levels, programming languages also define a fixity for most operators. This is called *associativity* in the context of programming. For example we say that the operators + and * in Java are “left-associative” to mean that they are grouped from the left. The following links provide operator precedence tables for different languages. **Check them out and see where they overlap and where they differ.** - Operator precedence in Java__ .. __: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html - Operator precedence in C++__ .. __: http://en.cppreference.com/w/cpp/language/operator_precedence - Operator precedence in Python__ .. __: https://docs.python.org/3/reference/expressions.html#operator-precedence - Operator precedence in R__ .. __: http://cran.r-project.org/doc/manuals/r-release/R-lang.html#Infix-and-prefix-operators - Operator precedence in Matlab and Simulink__ .. __: http://nl.mathworks.com/help/matlab/matlab_prog/operator-precedence.html Note in particular that some language place ordering comparison (< >) at the same level as the equality comparison, and others not. Right associativity =================== The reason why we need to care a bit more about associativity in programming than in math is that *some operators are right-associative*. For example, in Python, the arithmetic exponentiation noted ** is right-associative: the expression “2**3**4” is really equivalent to “2**(3**4)”. (Java does not have an operator for this.) In Java (and other languages from its family including C and C++), there are two imporant constructs that are right-associative: assignment and if-else. Right associativity of assignments ---------------------------------- The assignment in C, C++, Java and other languages is defined to be right-associative in order to simplify the assignment of multiple variables to the same value. So instead of writing:: x = 3; y = 3; z = 3; we can write:: x = y = z = 3; Which is equivalent to “x = (y = (z = 3))”. This works because the “result” of an assignment is the variable on the left of the equal sign. So “z = 3” sets z to the value 3 then gives z back into “y = z”, which changes y then gives y back to “x = y”. Of course all this would not work if the assignment was left-associative. Right associativity of if-else ---------------------------------- This is a dangerous pitfall which need particular attention. Consider the following program fragment: .. code:: java int x = 1; if (1 > 6) if (3 > 2) x = 2; else x = 3; What is the resulting value for x? The proper answer is 1, not 3! This is because if-else is right-associative: the else part must be grouped with the first “if” found from the right. So the example above is really equivalent to: .. code:: java int x = 1; if (1 > 6) { if (3 > 2) x = 2; else x = 3; } Combined operators ================== Most programming language provide *combined operators* to shorten expressions. The most well-known are “<=” and “>=”, from mathematics. We can draw the following equivalence table: ============ ======================= Operation Equivalent to ============ ======================= a <= b (a < b) || (a == b) a >= b (a > b) || (a == b) a != b !(a == b) ============ ======================= Combined assignment and arithmetic ---------------------------------- In nearly all programming languages with an assignment operator, the following pattern happens often in code: = Where the same variable name is expressed on the left and right side of “=”. For example:: i = i + 1 a = a * 2 z = z - 3 Since this pattern is so common, most language will provide combined assignment operators of the form: = For example in Java, C and C++ the following equivalences hold: ============ ======================= Operation Equivalent to ============ ======================= a += b a = a + b a -= b a = a - b a /= b a = a / b a *= b a = a * b a %= b a = a % b ============ ======================= Pre- and Post- increment and decrement -------------------------------------- The C language, which Java inherits from, also has two additional operators: ++ and --. They each exist in two forms: a *prefix* form “++x” and a *suffix* form “x++”, for a total of 4 operators. They are defined as follows: *Pre-increment and pre-decrement:* Syntax: ++ -- Semantics: Equivalent respectively to “ += 1” and “ -= 1”: the variable is incremented (resp. decremented) by 1, then the variable is returned as result into the enclosing expression. For example: .. code:: java int x = 42; int y = ++x; System.out.printf("%d %d\n", x, y); // prints 43 43 The prefix form is the simplest to understand at first contact, however in practice the suffix form is more commonly used: *Post-increment and post-decrement:* Syntax: ++ -- Semantics: 1. the current value of the variable is saved. 2. the variable is incremented (resp. decremented) by 1. 3. the value saved in step #1 is returned as result into the enclosing expression. For example: .. code:: java int x = 42; int y = x++; System.out.printf("%d %d\n", x, y); // prints 43 42 The name “pre-increment” comes from Latin “pre” for “before”: the variable is incremented before its value is given back in the enclosing expression. In “post-increment” the variable is incremented “after” its value is given back in the enclosing expression. Important concepts ================== - *operator precedence*; - *fixity* in math and *associativity* in languages; - how to read a precedence table; - right associativity of assignments in Java; - right associativity of if-else in Java; - combined operators; - pre- and post- increment and decrement. Further reading =============== - Think Java, section 2.7 (pp. 19-20), section 8.8 (p. 97) - Introduction to Programming, section 2.5 (pp. 47-51) - Absolute Java, Appendix 2 (p. 1141), increment & decrement operators (pp. 30-33) ---- Copyright and licensing ======================= Copyright © 2014, Raphael ‘kena’ Poss. Permission is granted to distribute, reuse and modify this document according to the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/ _.