Chapter 7 Expressions and Assignment Statements ISBN 0-321-49362-1 Chapter 7 Topics • • • • • • • • Introduction Arithmetic Expressions Overloaded Operators Type Conversions Relational and Boolean Expressions Short-Circuit Evaluation Assignment Statements Mixed-Mode Assignment Copyright © 2007 Addison-Wesley. All rights reserved. 1-2 Introduction • Expressions are the fundamental means of specifying computations in a programming language • To understand expression evaluation, we need to be familiar with the orders of operator and operand evaluation • The essence of imperative languages is the dominant role of assignment statements. Copyright © 2007 Addison-Wesley. All rights reserved. 1-3 Arithmetic Expressions • Arithmetic evaluation was one of the motivations for the development of the first programming languages. • Arithmetic expressions consist of operators, operands, parentheses, and function calls. Copyright © 2007 Addison-Wesley. All rights reserved. 1-4 Arithmetic Expressions: Design Issues • Design issues for arithmetic expressions – – – – – – operator precedence rules operator associativity rules order of operand evaluation operand evaluation side effects operator overloading mode mixing expressions Copyright © 2007 Addison-Wesley. All rights reserved. 1-5 Arithmetic Expressions: Operators • A unary operator has one operand • A binary operator has two operands • A ternary operator has three operands Copyright © 2007 Addison-Wesley. All rights reserved. 1-6 Arithmetic Expressions: Operator Precedence Rules • The operator precedence rules for expression evaluation define the order in which “adjacent” operators of different precedence levels are evaluated • Typical precedence levels – – – – – parentheses unary operators ** (if the language supports it) *, / +, - Copyright © 2007 Addison-Wesley. All rights reserved. 1-7 Arithmetic Expressions: Operator Precedence Rules A few interesting notes: • In Java and C# the unary plus and minus operators causes an implicit conversion of short or byte operands to ints. • Of course, unary minus would also reverse the sign of the operand. • Fortran, Ruby, Visual Basic and Ada have an exponentiation operator. – This operator has higher precedence than the unary minus operator so: • Example: - A ** B is the same as • Page 315 lists the precedence of arithmetic operators for a few of the common languages. – – • - ( A ** B) Note: In Ada’s rem operator is the same as the % operator in Ruby and the Cbased languages. The Ada mod operator is identical to rem when both operands are positive but can be different when one or both are negative. APL has a single level of precedence. Copyright © 2007 Addison-Wesley. All rights reserved. 1-8 Arithmetic Expressions: Operator Associativity Rule • The operator associativity rules for expression evaluation define the order in which adjacent operators with the same precedence level are evaluated • Typical associativity rules – Left to right, except **, which is right to left – Sometimes unary operators associate right to left (e.g., in FORTRAN) • In APL all operators associate right to left • Precedence and associativity rules can be overriden with parentheses Copyright © 2007 Addison-Wesley. All rights reserved. 1-9 Arithmetic Expressions: Operator Associativity Rule • Interesting notes: – In Fortran and Ruby the exponentiation operator is right associative. • a ** b ** c is evaluated as a ** (b ** c) – In Ada, exponentiation is non-associative, which menas that the expression a ** b ** c is illegal and must be written as • (a ** b) ** c or a ** (b ** c) – In Visual Basic the exponentiation operator, ^, is left associatve. – Pages 316-317 lists the associativity rules for a few common imperative languages. Copyright © 2007 Addison-Wesley. All rights reserved. 1-10 Arithmetic Expressions: Operator Associativity Rule • Interesting notes: – Optimization and associativity: Some compilers for the imperative languages can make use of the fact that some arithmetic operators are mathmatically associative to perform some optimizations. • In math ( A + B ) + C is always the same as A + ( B + C ) – But the fact that a mathematical operation is associative does not imply that the corresponding computer arithmetic is precisely associative. • In a computer, both floating-point representations and floating-point arithmetic are only approximations of the mathematical counterparts. • Can also be true for some integer operations, such as when adding very large numbers and very small numbers. – – A + B + C + D, where A and C are very large positive numbers and B and D are very large negative numbers. If optimizations are done yielding an ordering of (A + C) + ( B + D) then overflow… Use parentheses to show clear intent. (A + B ) + ( C + D) – In APL, Ken Iverson, made the choice that there would be no precedence rules and all operators would associate right to left. • The expression A X B + C is evaluated as A X ( B + C) Copyright © 2007 Addison-Wesley. All rights reserved. 1-11 Arithmetic Expressions: Operator Associativity Rule • Interesting notes: – In Ruby, a purely OOPL, every data value including literals is an object. – While Ruby supports the collection of arithmetic and logic operations of the C-based languages, all of these operators are implemented as methods. – a + b is actually a class to the + method of the object referenced by a, passing the object reference by b as a parameter. – Since methods can be overriden by application programs, these operators can be redefined. Copyright © 2007 Addison-Wesley. All rights reserved. 1-12 Arithmetic Expressions: Conditional Expressions • Conditional Expressions – C-based languages (e.g., C, C++) – An example: average = (count == 0)? 0 : sum / count – Evaluates as if written like if (count == 0) average = 0 else average = sum /count – Perl, JavaScript and Ruby also provide conditional expressions. Copyright © 2007 Addison-Wesley. All rights reserved. 1-13 Arithmetic Expressions: Operand Evaluation Order A less commonly discussed design characteristic of expressions is the order of evaluation of operands. • • 1. Variables: fetch the value from memory. 2. Constants: sometimes a fetch from memory; sometimes the constant is in the machine language instruction. 3. Parenthesized expressions: evaluate all operands and operators first. If neither of the operands of an operator has side effects, then operand evaluation order is irrelevant. The Java language definition guarantees that operands appear to be evaluated in left-to-right order, eliminating side effects. Copyright © 2007 Addison-Wesley. All rights reserved. 1-14 Arithmetic Expressions: Operand Evaluation Order Potentials for Side Effects • Functional side effects: – Occurs when a function changes a two-way parameter or a nonlocal (global) variable. • Problem with functional side effects: – When a function referenced in an expression alters another operand of the expression; e.g., for a parameter change: a = 10; /* assume that fun returns 10 but changes its parameter to 20 */ b = a + fun(a); Scenario 1: a is fetched first, then added to fun(a) result is 20 Scenario 2: fun(a) is evaluated first, then a is fetched result is 30 Copyright © 2007 Addison-Wesley. All rights reserved. 1-15 Arithmetic Expressions: Operand Evaluation Order - Potentials for Side Effects • Functional side effects: – Occurs when a function changes a two-way parameter or a nonlocal (global) variable. • C example of functional side effects: int a = 5; int fun1() { a = 17; return 3; } /* of fun 1 */ // global variable // change to global void main() { a = a + fun1(); // value of a depends on order of evaluation } /* of main */ Scenario 1: value of a is fetched first result will be 8. Scenario 2: function evalated first, result will be 20. Copyright © 2007 Addison-Wesley. All rights reserved. 1-16 Arithmetic Expressions: Operand Evaluation Order - Potentials for Side Effects Functional Side Effects • Two possible solutions to the problem 1. Write the language definition to disallow functional side effects • • • • No two-way parameters in functions No non-local references in functions Advantage: it works! Disadvantage: inflexibility of two-way parameters and non-local references 2. Write the language definition to demand that operand evaluation order be fixed • • Disadvantage: limits some compiler optimizations Note that purely functional languages do not have this problem. Copyright © 2007 Addison-Wesley. All rights reserved. 1-17 Arithmetic Expressions: Operand Evaluation Order - Potentials for Side Effects • • Referential Transparency – a property whereby any two expressions that have the same value can be substituted for one another anywhere in the program without affecting the action of the program. Example: – – – • Advantages: – – • result1 = (fun(a) + b) / (fun(a) – c); temp = fun(a); result2 = (temp + b ) / (temp – c); Readability and reliability. Programs can be reasoned about mathematically. Note that purely functional languages have this property. Copyright © 2007 Addison-Wesley. All rights reserved. 1-18 Overloaded Operators • Use of an operator for more than one purpose is called operator overloading • Common example: + for int and float addition • Some have the potential for errors – In C the use of the operator & for address-of along with logical bitwise AND operations. – Loss of compiler error detection (omission of an operand should be a detectable error) – Some loss of readability • These types of problems can be avoided by using distinct operator symbols for differing operations. – Example: Pascal’s div for integer division. • Good decision or bad decision? – JavaScript avoids this problem by not having any integer arithmetic. – In PHP if the result of int division yields a fractional part then the result is a floating point vlaue. Copyright © 2007 Addison-Wesley. All rights reserved. 1-19 Overloaded Operators (continued) • C++ and Ada allow user-defined overloaded operators. • Potential problems: – Users can define nonsense operations such as multiplication for +. – Readability may suffer, even when the operators do make sense. – • Consider A * B + C * D for • MatrixAdd(MatrixMult(A, B), MatrixMult(C, D)) Copyright © 2007 Addison-Wesley. All rights reserved. 1-20 Type Conversions • 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., float to int – • 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 – • In java: • short to byte or char • char to byte or short • int to byte, short or char • long to byte, short, char or int • float to byte, short, char or int, or long • double to byte, short, char or int, long, or float In java: • byte to short, int, long, float, or double • …. Note that even a widening conversion can be problematic. – Consider a 32 bit int conversion to 32 bit float. • The 9 decimal digits of precision for int converted to only seven decimal digits of precision of the float. Copyright © 2007 Addison-Wesley. All rights reserved. 1-21 Type Conversions: Mixed Mode • A mixed-mode expression is one that has operands of different types. • A coercion is an implicit type conversion. • Disadvantage of coercions: • In most languages, all numeric types are coerced in expressions, using widening conversions. – – A decrease in the ability of the compiler to detect “type errors” Java Example: int a; float b, c, d; … d = b * a; • // Okay in java In Ada, there are virtually no coercions in expressions. A : Integer; B, C, D : Float; … C := B * A; Copyright © 2007 Addison-Wesley. All rights reserved. Error in Ada 1-22 Explicit Type Conversions • Explicit Type Conversions • Called casting in C-based language • Examples – C: (int) angle – Ada: Float(sum) Note that Ada’s syntax is similar to function calls Copyright © 2007 Addison-Wesley. All rights reserved. 1-23 Type Conversions: Errors in Expressions • Errors in the evaluation of expressions can occur in several cases. – In cases of type coercions. – • If a languages requires type checking then operand errors cannot occur. – Inherent limitations of arithmetic • division by zero in Math is not possible. – Limitations of computer arithmetic • Overflow or Underflow – These types of exceptions can be ignored by the run-time system. • Some languages provide facilities for the detection and handling of these exceptions. Copyright © 2007 Addison-Wesley. All rights reserved. 1-24 Relational and Boolean Expressions • Relational Expressions – Use relational operators and operands of various types – Evaluate to some Boolean representation – Operator symbols used vary somewhat among languages • >, <, .GT., .LT., etc. • JavaScript and PHP – “7” == 7 vs. “7” === 7 • Ruby uses == for equality with coercions and eq1? For equality tests without coercions. – In general, relational operators have lower precedence than the arithmetic operators. • a+1>2*b – Page 328 shows the syntax of relational operators in some common PLs. Copyright © 2007 Addison-Wesley. All rights reserved. 1-25 Relational and Boolean Expressions • Boolean Expressions – Operands are Boolean and the result is Boolean – Example operators FORTRAN 77 FORTRAN 90 C Ada .AND. .OR. .NOT. and or not && || ! and or not xor Copyright © 2007 Addison-Wesley. All rights reserved. 1-26 Relational and Boolean Expressions: No Boolean Type in C • C has no Boolean type--it uses int type with 0 for false and nonzero for true. • One odd characteristic of C’s expressions: – a < b < c is a legal expression, – but the result is not what you might expect: • The left operator is evaluated, producing 0 or 1 – The evaluation result is then compared with the third operand (i.e., c) • Consider: 8 < 7 < 1 Copyright © 2007 Addison-Wesley. All rights reserved. 1-27 Relational and Boolean Expressions: • Some languages provide two sets of the binary logic operators. • Perl and Ruby provide – two forms of AND – && – and (has lower precedence than its && counterpart) – two forms of OR – || – or (has lower precedence than its || counterpart) – Also, the precedence of && is higher than ||, while the spelled out forms have equal precedence! Copyright © 2007 Addison-Wesley. All rights reserved. 1-28 Relational and Boolean Expressions: Operator Precedence • Precedence of C-based operators prefix ++, -unary +, -, prefix ++, --, ! *,/,% binary +, <, >, <=, >= =, != && || Copyright © 2007 Addison-Wesley. All rights reserved. 1-29 Short Circuit Evaluation • Definition: an expression in which the result can be determined without evaluating all of the operands and/or operators. • Example: (13*a) * (b/13–1) If a is zero, the result will be 0, since 0 times any number is zero, so there is no need to evaluate (b/13-1) However, this type of shortcut is never taken since it cannot be detected very easily. • Example: (a >= 0) && (b < 10) If a is less than 0, making a >= 0 false, there is no need to evaluate (b < 10). This shortcut can be easily discovered during execution. • Problem with languages that do not provide for non-short-circuit evaluation index = 1; while (index <= length) && (LIST[index] != value) index++; If a language provides short-circuit evaluation of Boolean expressions, then this is okay. • Potential problem with languages that provide short-circuit evaluation and side-effects in expressions. – (a > b) || ( ( b++) / 3) – These problems can be solved by providing additional operators that will cause full-evaluation of an expression to occur in all cases. • • Example the C-based languages use & and | for this purpose. Ada provides the two-word operators and then and or else to specify that short-circuit evaluation is to be used. Index := 1; While (Index <= Listlen) and then (List (Index) /= Key) loop Index := Index + 1; end loop; • All of the operators in Ruby and Perl are short-circuit. Copyright © 2007 Addison-Wesley. All rights reserved. 1-30 Assignment Statements • The general syntax <target_var> <assign_operator> <expression> • The assignment operator = FORTRAN, BASIC, PL/I, C, C++, Java := ALGOLs, Pascal, Ada • Obviously, using = as the assignment operator and overloaded as the relational operator for equality can lead to many errors and therefore a bad design decision. • Example: if (x = y) will not be detected as an error in this case. • In the C-based languages the = operator can be embedded in expressions. – a = b = c; is possible along with if ( a > (b = c) ) … • Design choices have varied widely. – Fortran and Ada require that an assignment can only appear as a standalone statement and the destination must be a single variable. Copyright © 2007 Addison-Wesley. All rights reserved. 1-31 Assignment Statements: Conditional Targets • Conditional targets (C, C++, and Java) (flag)? total : subtotal = 0 Which is equivalent to if (flag) total = 0 else subtotal = 0 Copyright © 2007 Addison-Wesley. All rights reserved. 1-32 Assignment Statements: Compound Operators • A shorthand method of specifying a commonly needed form of assignment • Introduced in ALGOL; adopted by C • Example a = a + b is written as a += b Copyright © 2007 Addison-Wesley. All rights reserved. 1-33 Assignment Statements: Unary Assignment Operators • Unary assignment operators in C-based languages combine increment and decrement operations with assignment • Examples sum = ++count (count incremented, added to sum) sum = count++ (count incremented, added to sum) count++ (count incremented) -count++ (count incremented then negated due to right to left associativity) Copyright © 2007 Addison-Wesley. All rights reserved. 1-34 Assignment as an Expression • In C, C++, and Java, the assignment statement produces a result and can be used as operands • An example: while ((ch = getchar())!= EOF){…} – – • first the assignment statement ch = getchar() is executed (due to the parentheses) then the result (assigned to ch) is used as a conditional value for the while statement The disadvantage of allowing this type of side effect can hinder readability. – Example: the previous expression must be read as a list of instructions… – Example: a = b + (c = d / b++) -1; Copyright © 2007 Addison-Wesley. All rights reserved. 1-35 List Assignments • Perl and Ruby provide multiple target, multiplesource assignemnt statements. • Example in Perl: – ($first, $second, $third) = (20, 40, 60); – ($first, $second) = ($second, $first); – Excess variables on the rhs are ignored as in: • ($first, $second, $third) = (20, 40, 60, 70); – Excess variables on the lhs are set to undef as in: – ($first, $second, $third, $fourth) = (20, 40, 60); Copyright © 2007 Addison-Wesley. All rights reserved. 1-36 Mixed-Mode Assignment • Assignment statements can also be mixed-mode. That is the type of the rhs is not the same as that of the lhs. • For example, the rules for the code below will depend upon the design decisions made for the particular language: int a, b; float c; c = a / b; • Fortran, C, C++ and Perl use coercion rules similar to those for mixed-mode expressions. • In Pascal, integer variables can be assigned to real variables, but real variables cannot be assigned to integers. • In Java and C#, only widening assignment coercions are done. • In Ada, there is no assignment coercion. • In Python and Ruby, types are associated with objects, not variables, so there is no such thing as mixed-mode assignment. Copyright © 2007 Addison-Wesley. All rights reserved. 1-37 Summary • • • • • Expressions Operator precedence and associativity Operator overloading Mixed-type expressions Various forms of assignment Copyright © 2007 Addison-Wesley. All rights reserved. 1-38