Expressions and Assignment Statements Programming Languages
Expressions and Assignment Statements Programming Languages
1
Introduction
2. "To understand expression evaluation, need to be familiar with the orders of operator and operand
evaluation."
To understand how the computer solves an expression, you need to know which parts it looks at first — like which
operator (e.g., +, *) or value (called an operand) it uses first.
2
Arithmetic Expressions
Arithmetic evaluation was one of the motivations for the development of the first
programming languages.
An operator can be unary, meaning it has a single operand, binary, meaning it has
two operands, or ternary, meaning it has three operands.
– C-based languages include a ternary operator, which has three operands
(conditional expression).
EX: x = (a > b) ? a : b;
- If a is greater than b, then x gets a; otherwise, x gets b.
"Arithmetic evaluation was one of the motivations for the development of the first programming languages."
One reason early programming languages were made was to help computers solve math problems (like addition, subtraction, etc.).
"Most of the characteristics of arithmetic expressions in programming languages were inherited from conventions that had evolved in
mathematics."
The way we write math expressions in programming (like a + b * c) mostly follows math rules we already use, like order of operations.
"Arithmetic expressions consist of operators, operands, parentheses, and function calls."
A math expression in code usually has:
•Operators (like +, -, *, /)
•Operands (the values or variables being used)
•Parentheses (to group parts of the expression)
•Function calls (like sqrt(9) or pow(2, 3))
"An operator can be unary, meaning it has a single operand, binary, meaning it has two operands, or ternary, meaning it has three
operands.“
Operators come in different types:
•Unary: uses 1 value (like -x)
•Binary: uses 2 values (like a + b)
•Ternary: uses 3 values
"C-based languages include a ternary operator, which has three operands (conditional expression)."
Programming languages like C, Java, or JavaScript have a special ternary operator used for conditions.
3
Arithmetic Expressions
4
Design Issues for arithmetic expressions
5
Operator Evaluation Order
Precedence
Associativity
Parentheses
Expressions in Lisp
Conditional Expressions
6
Precedence
– The operator precedence rules for expression evaluation define the order in
which the operators of different precedence levels are evaluated.
– Unary addition (+) is called the identity operator because it usually has no
associated operation and thus has no effect on its operand.
– In Java and C#, unary minus also causes the implicit conversion of short and
byte operands to int type.
– In all of the common imperative languages, the unary minus operator can
appear in an expression either at the beginning or anywhere inside the
expression, as long as it is parenthesized to prevent it from being next to
another operator.
1. "The operator precedence rules for expression evaluation define the order in which the operators of different
precedence levels are evaluated.“
Precedence decides which operator goes first when there are multiple kinds in one expression.
Example: In 2 + 3 * 4, multiplication happens before addition.
2. "Many languages also include unary versions of addition and subtraction."
Some languages allow + or - with just one value, like -x or +x. These are called unary operators.
3. "Unary addition (+) is called the identity operator because it usually has no associated operation and thus has no effect
on its operand."
Writing +x does nothing — it’s like saying, “keep it the same.” That’s why it’s called the identity operator.
4. "In Java and C#, unary minus also causes the implicit conversion of short and byte operands to int type."
In Java or C#, when you use - on smaller number types (like byte or short), they automatically turn into int type before doing
the math.
5. "In all of the common imperative languages, the unary minus operator can appear in an expression either at the
beginning or anywhere inside the expression, as long as it is parenthesized to prevent it from being next to another
operator."
You can use - to make a number negative anywhere in an expression, but if it’s near another operator, use parentheses to
avoid confusion.
Example: x * (-y) is OK, but x * - y without parentheses may be unclear.
7
Precedence
A + (- B) * C // is legal
A+-B*C // is illegal
-A ** B
Is equivalent to
-(A ** B)
1. A + (-B) * C is legal
This is correct because -B is inside parentheses, so it's clear you're using unary minus on B.
2. A + - B * C is illegal
This can be confusing to the compiler because there's no parentheses, and it’s not clear how to handle - B * C.
Example:
If A = 2, B = 3, then:
-A ** B = -(2 ** 3) = -8
8
Precedence
9
Associativity
– The operator associativity rules for expression evaluation define the order in
which adjacent operators with the same precedence level are evaluated. An
operator can be either left or right associative.
– Ex: Java
When two or more operators have the same strength (same precedence), which one happens first?
10
Associativity
– Ex: Fortran
A ** B ** C // right to left
(A ** B) ** C // in Ada it must be parenthesized
11
Associativity
– The associativity rules for a few common languages are given here:
12
Associativity
– APL is different; all operators have equal precedence and all operators
associate right to left.
– Ex: APL
AxB+C // A = 3, B = 4, C = 5 27
13
Parentheses
– Ex:
14
Expressions in Lisp
– Ex: to specify the c expression a + b * c in Lisp, one must write the following
expression:
15
Conditional Expressions
if (count == 0)
average = 0;
else
average = sum / count;
16
Conditional Expressions
– Ex:
instead of writing a long if-else, you can use a ternary operator ? : for shorter code.
17
Operand evaluation order
When you use a variable (like x or count), the computer fetches (gets) its value from memory.
•Example: If x = 5, when you write y = x + 2, the computer looks up x in memory, finds 5, and uses it.
Constants:
Constants are fixed values (like 5, 10, or 'A').
•Sometimes, the constant is already inside the machine's instruction (no need to fetch from memory).
•Sometimes, if it's a big or special constant, it might still be fetched from memory.
18
Side Effects
A side effect of a function, called a functional side effect, occurs when the
function changes either one of its parameters or a global variable.
Ex: a + fun(a)
• If fun does not have the side effect of changing a, then the order of
evaluation of the two operands, a and fun(a), has no effect on the
value of the expression. However, if fun changes a, there is an effect.
EX: a + fun(a)
BUT
If fun(a) changes a (like setting a = 0 inside fun):
•Then order matters!
•Because a's value changes before or after the addition, causing different results.
Why is it important?
If functions have side effects, programs can behave differently based on what gets evaluated first.
19
Side Effects
Ex:
Consider the following situation: fun returns 10 and changes its parameter
to have the value 20, and:
a = 10;
b = a + fun(a);
Given:
•fun(a) returns 10
•fun(a) changes a to 20
20
Side Effects
• If the value of a is fetched first (in the expression evaluation process), its
value is 10 and the value of the expression is 20 (a + fun(a)= 10 + 10).
• But if the second operand is evaluated first, then the value of the first
operand is 20 and the value of the expression is 30 (a + fun(a)= 20 + 10).
21
Side Effects
Functional side effects occur when a function modifies something (like a parameter or a global variable) while also returning a result. This
can cause confusion because the order of evaluation matters — the program might behave differently depending on which part of the
expression is evaluated first.
Two Possible Solutions to This Problem:
1.Disallow functional side effects in the language
1. The language design could prohibit functions from modifying parameters or global variables. This would eliminate side
effects and ensure that functions are pure (i.e., they always return the same output for the same input and don’t change
anything else).
2. Example:
If side effects are not allowed, a function like fun(a) in C would not be allowed to change a. It would just return a value and
leave everything else alone.
2. Fix operand evaluation order in the language
•The language can define a strict order for evaluating operands in expressions. For example, Java guarantees that operands are always
evaluated in left-to-right order.
•This means in Java, the expression a + fun(a) will always evaluate a first, and then fun(a), making the evaluation predictable and
consistent. You don’t have to worry about the order causing problems.
In Java, the order of operand evaluation is fixed as left-to-right. This means:
•When you write a + fun(a), Java will always evaluate a first, then fun(a).
•Even if fun(a) changes a, Java ensures that the operands are evaluated in a predictable sequence, which avoids unexpected results due
to side effects.
22
Overloaded Operators
The use of an operator for more than one purpose is operator overloading.
Operator overloading happens when the same operator (like +, &, *, etc.) is used for different purposes
depending on the context.
The + operator:
•In Java, the + operator is used both for addition and for string concatenation.
23
Overloaded Operators
– Some loss of readability to use the same symbol for two completely
unrelated operations.
– The simple keying error of leaving out the first operand for a bitwise AND
operation can go undetected by the compiler “difficult to diagnose”.
In C and C++, the & operator has two very different uses, which can cause confusion:
24
Overloaded Operators
C++, C#, and F# are programming languages that allow programmers to define their own behavior for operators
(like +, -, *, etc.) when applied to custom types (such as classes or structs). This process is called operator
overloading. For example, you can define how the + operator should behave when used with objects of a class you
created.
"When sensibly used, such operators can be an aid to readability (avoid method calls, expressions appear
natural)."
If operator overloading is used correctly, it can make code easier to read. Instead of calling a method like
object.add(otherObject), you can simply use an operator like object + otherObject. This makes the code look more
natural and similar to how we write regular arithmetic expressions, improving readability and clarity.
25
Activity – Essay
1. Programming languages have rules about which
operations happen first (like multiplication before
addition). Do you think these rules make
programming easier, or would it be better if it was
more "what you see is what you get"?
26
Type Conversions
Coercion in Expressions
– A mixed-mode expression is one that has operands of different types.
– A coercion is an implicit type conversion.
– Disadvantage of coercions:
• They decrease in the type error detection ability of the compiler
"A narrowing conversion is one that converts an object to a type that cannot include all of the values of the
original type e.g., double to float."
A narrowing conversion happens when you convert a value to a type that cannot represent all the values of the
original type. This usually happens when you are trying to fit a larger type into a smaller type, which might cause
loss of information.
"A widening conversion is one in which an object is converted to a type that can include at least approximations
to all of the values of the original type e.g., int to float."
A widening conversion is the opposite of narrowing. It happens when you convert a value to a type that can hold
at least all the values from the original type, or at least close approximations of them. This type of conversion
increases the range of values.
• Coercion is the process of automatically converting a value from one type to another
• In the expression 5 + 3.2, 5 is an int and 3.2 is a float, so this is a mixed-mode expression.
• Coercion refers to an implicit type conversion, meaning the compiler or interpreter automatically converts
one type to another in situations where necessary, without the programmer’s explicit request.
27
Type Conversions
"In most languages, all numeric types are coerced in expressions, using widening conversions."
In most programming languages, when you have expressions involving numbers of different types (like int, float,
double), the language automatically converts the smaller types into the larger ones (widening conversions). This
ensures that the expression can work correctly even if the numbers are of different types.
28
Type Conversions
3. "Language designers are not in agreement on the issue of coercions in arithmetic expressions."
The choice of whether to allow automatic coercion in arithmetic expressions is a debated issue among language designers. Some prefer it, while others don't, and there are
valid concerns on both sides. There isn't a universal agreement on the best approach.
4. "Those against a broad range of coercions are concerned with the reliability problems that can result from such coercions, because they eliminate the benefits of type
checking."
Some programmers and language designers oppose broad coercions (automatic type conversions) because they believe it can lead to reliability issues. If the compiler
automatically converts types in ways that might not be obvious, it can cause bugs or unexpected behavior that are hard to detect. This also undermines the benefits of
type checking, which is supposed to catch type-related errors.
• Example:
If a language automatically converts an integer to a float in an operation like 5 + "3.2", you might end up with a result that's not what you expected. The
problem is that you didn't explicitly tell the language how to handle this conversion, and the error might slip through unnoticed.
5. "Those who would rather include a wide range of coercions are more concerned with the loss in flexibility that results from restrictions."
On the other hand, some language designers favor including more coercions because they think that restricting them too much can reduce flexibility in programming. With
more automatic conversions, programmers can write more concise and flexible code without having to worry about explicitly converting types every time they mix different
types in expressions.
• Example:
If you want to add an int and a float, having automatic coercion means you don’t have to manually convert the int to a float each time. This can make the
code more flexible and easier to write, especially in complex calculations.
6. "The issue is whether programmers should be concerned with this category of errors or whether the compiler should detect them."
The core debate revolves around whether programmers should actively manage the potential errors that come from type coercion (by explicitly converting types) or if the
compiler should take care of it automatically, allowing the programmer to focus on the logic of the program without worrying about every little detail of type conversion.
• Example:
Should the programmer manually check and convert types like int to float when needed, or should the compiler just handle it automatically and report
errors if something goes wrong?
29
Type Conversions
– Ex: Java
int a;
float b, c, d;
...
d = b * a;
• Because mixed-mode expressions are legal in Java, the compiler would not
detect this as an error. Simply, b will be coerced to float.
30
Explicit Type Conversions
Ex: In Java, to specify a cast, the desired type is placed in parentheses just
before the expression to be converted, as in
(int)angle
31
Relational Expressions
"A relational operator: an operator that compares the values of its two operands."
•Explanation:
A relational operator is used to compare two values. The result of this comparison is usually a true or false value (Boolean), depending on
whether the comparison is valid or not. These operators typically check for relationships like equality, inequality, greater than, less than,
etc.
• Examples of relational operators:
• == (equal to)
• != (not equal to)
• < (less than)
• > (greater than)
• <= (less than or equal to)
• >= (greater than or equal to)
"Operator symbols used vary somewhat among languages (!=, /=, .NE., <>, #)"
•Explanation:
The symbols for relational operators can vary between different programming languages. While some languages use standard symbols
like != for "not equal to" or == for "equal to," others may use different representations.
• Example:
• != (not equal to) in languages like C, Java, and Python
• /= (not equal to) in languages like Python and Pascal
• .NE. (not equal to) in languages like Fortran
• <> (not equal to) in languages like SQL and older versions of Visual Basic
• # (not equal to) in some variants of assembly language
32
Relational and Boolean Expressions
33
Relational and Boolean Expressions
JavaScript and PHP have two additional relational operator, === and !==
– Similar to their cousins, == and !=, except that they do not coerce their
operands
34
Boolean Expressions
35
Boolean Expressions
Versions of C prior to C99 have no Boolean type; it uses int type with 0 for
false and nonzero for true.
a>b>c
36
Short-Circuit Evaluation
Ex:
(13 * a) * (b/13 – 1)
Short-circuit evaluation is a technique where the evaluation of an expression stops as soon as the result is
determined.
A short-circuit evaluation occurs when the program can determine the result of an expression early, without
evaluating all parts of the expression. This can happen when the result of a certain part of the expression is enough
to determine the overall result, and further evaluation becomes unnecessary.
EX: In an OR (||) expression, if the first operand is true, then the whole expression will be true regardless of the
second operand. So, the second operand isn't evaluated (this is short-circuiting).
37
Short-Circuit Evaluation
•If a < 0, then the first part of the expression, (a >= 0), becomes false.
• In a logical AND expression, if the first operand is false, the entire expression will always be false,
regardless of the value of the second operand.
• So, when a < 0, the value of the second expression (b < 10) doesn't matter because the whole
expression is already determined to be false.
38
Assignment Statements
Simple Assignments
"The C-based languages use == as the equality relational operator to avoid confusion with their assignment
operator“
In C-based languages (like C, C++, Java), the equality operator is == (double equals). This is different from the
assignment operator =.
• The reason for this distinction is to avoid confusion between the two operations.
• == is used to compare two values (e.g., a == b checks if a is equal to b).
• = is used to assign a value to a variable (e.g., a = 5 assigns the value 5 to a).
•This distinction is crucial because using the wrong operator (e.g., using = instead of ==) would lead to logical errors
in the program.
39
Assignment Statements
Conditional Targets
Ex: Perl
In programming, conditional targets typically refer to the concept where the result of an expression or operation
depends on a condition, and the action or value chosen is determined by that condition.
In Perl, conditional targets often relate to how the language handles conditional statements and expressions to
determine which block of code should be executed.
Perl has a variety of ways to handle conditional logic, including if, elsif, and else constructs. Additionally, Perl
supports conditional expressions like the ternary operator (?:), which is used to assign values based on a condition.
40
Assignment Statements
a=a+b
A compound assignment operator is a shorthand way of writing expressions that involve the destination variable
being used on both sides of an assignment. The idea is to make the code more concise and readable.
•The basic form of a compound assignment operator combines a binary operation with the assignment operator
(=). It allows you to perform the operation and assign the result to the same variable in one step.
•Instead of writing the full expression like a = a + b, you can use a shorthand version like a += b, which saves space
and improves readability.
41
Assignment Statements
A unary operator is called so because it operates on only one operand. The term "unary" comes from the Latin
word "unus," meaning one. In programming, unary operators take a single value or operand, modify it, and return a
result.
In C-based languages, there are two special unary operators used for incrementing and decrementing a variable’s
value, which can be combined with assignment operations.
These operators are ++ (increment) and -- (decrement), and they provide a shorthand way of updating a variable's
value in a single operation.
42
Assignment Statements
Assignment as an Expression
This design treats the assignment operator much like any other binary
operator, except that it has the side effect of changing its left operand.
Ex:
int a, b, c;
a = 5; // Assignment
b = (c = 10) + 2; // Assignment as part of an expression
Assignment as an Expression is a programming design approach where the assignment operator (=) is treated like
a regular binary operator. In this design, the assignment not only updates the value of the left operand but also
returns a value (usually the value assigned). This means that an assignment can be used as part of a larger
expression, allowing it to participate in computations or be chained with other operations.
(c = 10): The value 10 is assigned to c. Since the assignment operator returns the value being assigned, (c = 10)
evaluates to 10.
(c = 10) + 2: After assigning 10 to c, the expression becomes 10 + 2, which evaluates to 12.
b = 12: Finally, the value 12 is assigned to b.
43
Assignment Statements
Assignment as an Expression
44
Assignment Statements
Assignment as an Expression
in C, you can use the assignment operator (=) not just to assign a value to a variable, but also as part of a larger
expression.
If ( x = y )
Instead of checking if x is equal to y, it assigns y to x, and the condition will always be true, which might not be
what the programmer intended.
45
Assignment Statements
Multiple Assignments
Ex: Perl
46
Assignment Statements
Ex: in ML, names are bound to values with the val declaration, whose form
is exemplified in the following:
In functional programming languages, assignment is handled differently than in imperative languages (like C or
Java).
47
Mixed-Mode Assignment
Mixed-Mode Assignment
A mixed-mode assignment occurs when the value being assigned to a variable is of a different type than the variable itself. In this case,
the language may automatically convert or coerce the value to the correct type before the assignment is made.
For example:
•You might assign an integer value to a floating-point variable (e.g., int to float).
•Or assign a larger numeric type to a smaller one (e.g., double to int).
How Different Languages Handle Mixed-Mode Assignment
1.Fortran, C, and C++:
1. In these languages, you can assign any numeric value (like an integer, float, or double) to any numeric scalar variable (such
as a variable of type int, float, etc.).
2. The language automatically converts the value to the appropriate type for the target variable. This may involve narrowing
(e.g., converting from double to int) or widening (e.g., converting from int to float) conversions.
Java and C#:
•In Java and C#, only widening assignment coercions are allowed.
•This means that smaller types (e.g., int) can be assigned to larger types (e.g., float or double) without error, but the reverse (narrowing
conversion) requires explicit casting.
Ada:
•Ada is stricter about type safety and does not allow assignment coercion.
•This means you cannot automatically assign a value of one type to a variable of a different type, even if it’s numerically compatible. If you
need to assign a value of one type to another, you must use explicit type conversion (casting) functions.
48
Mixed-Mode Assignment
int a, b; float c;
...
c = a / b;
49
Summary
50
Thank you
51