Expression: Any valid combination of symbols that yields a value.Example: Some expressions
40 \text{$+$} 2 # 423 \lt 7 # true
In most mainstream imperative languages, expressions can be classified in two types (though, some languages may blur the boundaries):
Note — Other kinds of expressions may yield objects (e.g., Strings)
Most expressions are composed of operators processing operands. Operators may be classified in several ways:
The arity of an operator is the number of operands it needs to yield a value.
# Unary++x*pnot a# Binary5 \text{$+$} 27a or ba == b# Ternaryx = expr1 ? expr2: expr3
operator Positioning: Relative position of the operator wr.t. the operands
# Prefix++x# Suffixx++# Infix1 \text{$+$} 2
It is very important to define this in a language!
Suppose:
25 \text{$+$} 20 \text{$-$} 3
We could evaluate right-to-left or left-to-right. While these yield the same value, the programmer should know how is evaluated.
Example — Java expressions are generally left-to-right, like most languages.
This example is more ambiguous.
25 \text{$+$} 20 \large{*} 3
We can introduce parenthesis to force PEMDAS.
25 \text{$+$} (20 \large{*} 3)
Of course, most languages implement operator precedence so we don’t have to do this.
Hierarchy that specifies the relative strength with which operators bind to operands, determining order of evaluation. Most languages do this precedence:Example: Simple arithmetic precedence
Associativity rules must also be specified whether association is left-to-right or right-to-left. How does addition associate? Which plus is evaluated first? If you associate from the left, the first plus (5+4) runs first, and vice versa. This must be defined in the languages, especially if you have expressions that have side-effects.Example: Associativity
5 \text{$+$} 4 \text{$+$} 3
An expression with side-effects produces a destructive of memory.
Definition — A side effect is any destructive update of memory during a program.
Simple Example:
y := 0x := 1z := (x++) \text{$+$} y \text{$+$} x
When this executes, z will be modified. But before that happens, something else in touched. In this case, x is touched, which is a side-effect.
If we evaluate left-to-right, we get z = 1+0+2= 3, x = 2.
If we evaluate right-to-left, we get z = 1+0+1= 2, x = 2.
Another Example:
Here is an wonky expression that breaks the commutativity of or.
# Behavior: if x \gt 0, y incrementsx \gt 0 || y++ \gt 0# Behavior: y always incrementsy++ \gt 0 || x \gt 0
Lesson — Side-effects are evil and lead to error-prone code.
An expression is referentially transparent when substituting the expression with the value it yields doesn’t change the program’s behavior.Example: Referential Transparency
# Referentially Transparentx = 1 \text{$+$} 2# Not Referentially Transparentx = (x++) \text{$+$} 2
Operator is overloaded if it has more than one operation depending on the type of operands. In Java, we can overload methods. Our abstract idea is “the function has different behaviors/parameters depending on how we use it”. Likewise, operators can be overloaded.Example: Operator Overloading
// Adding floats
3 + 4
// Adding integers
1.0 + 5.6
// Concatenating strings
"Good" + " " + "Dog"
An expression is mixed-mode when it includes operands of different types.
Designer Choices:
# Intint x := 42# Floatint y := 1.6# Mixed-Modeint z := x \text{$+$} 3 \large{*} y \text{$-$} 2
Note — You must design the rules clearly!
- A lot of languages won’t let you do this.
- And languages that do, disallow precision loss like above (float \to int), unless the programmer explicitly casts the value.
Overflow: When an operation on an integer goes past the maximum representable value.
Underflow: When an operation on a float goes below the smallest representable value. Some languages error, some just let it happen.Example: Overflow and Underflow
# Overflowint x = 7;x ^ 2^{32}# Underflowfloat y = 7.0;y^{-64}
Relational Operators:
Logical Operators:
Form of lazy evaluation in which the second operand of a binary operation is evaluated if the first operand is true.
Definition — Lazy Evaluation: Any technique that delays the evaluation of an expression as much as possible to save.
- Opposite: Eager Evaluation
# If x is false, we can skip evaluating 2nd operandx and y# If x is true, we can skip evaluating 2nd operandx or y
Note — This does not play well with side-effects.