<<

9/15/2020

Chapter 6:: Control Flow Control Flow • control flow or ordering – fundamental to most models of computing – determines ordering of tasks in a program

Programming Language Pragmatics Michael L. Scott

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Control Flow Control Flow • categories for control flow: • basic categories for control flow (cont.): – sequencing: order of : expression defined in terms of – selection (also alternation): choice among two (simpler versions of) itself or more statements or expressions – concurrency: two or more program fragments • if or case statements are executed at the same time – iteration: loops • in parallel on separate processors • for, do, while, repeat • interleaved on a single processor – procedural abstraction: parameterized – and speculation – nondeterminacy: order or choice is deliberately left unspecified

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Control Flow Control Flow • previous eight categories cover all of the • importance of different categories varies control-flow constructs in most across paradigms programming languages – sequencing central in imperative and objected- • better to think in these categories rather oriented languages, but less important in than the specifics of a single programming functional languages language – functional languages use recursion heavily, while imperative languages focus more on – easier to learn new languages iteration – evaluate tradeoffs among languages – logic languages hide control flow entirely and – design and evaluate allow the system to find an order in which to Copyright © 2009 Elsevier Copyright © 2009 Elsevier apply inference rules

1 9/15/2020

Expression Evaluation Prefix, Infix, and Postfix Notation • expression consists of a simple object • some languages impose an ordering for (literal, variable, constant) or an operator or operators and their operands function call – prefix: op a b or op(a,b) • Lisp: (* (+ 1 3) 2) – function: my_func (A, B, ) • Cambridge prefix: function name inside parentheses; also used – operators: simple syntax, one or two operands with multiple operands: (+ 2 4 5 1) • a + b – infix: a op b • -c • standard method – sometimes operators are syntactic sugar • C: a = b != 0 ? a/b : 0 • in C++, a + b short for a.operator+(b)

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Prefix, Infix, and Postfix Notation Expression Evaluation • some languages impose an ordering for • arithmetic and logic operations may be operators and their operands (cont.) ambiguous without parentheses – postfix: a b op – : a + b * c****e/ • least common - used in Postscript, Forth, and intermediate code of some compilers • languages set precedence and associativity • C (and its descendants): x++ rules to determine order of operations • Pascal: pointer dereferencing operator (^) – precedence rules: order of types of operations – prefix and postfix sometimes referred to as Polish • 2 + 3 * 4 (14 or 20?) prefix and Polish postfix after Polish logicians who studied and popularized them – associativity rules: order of operations at same precedence

• 9 – 3 – 2 (4 or 8?) Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Expression Evaluation Expression Evaluation • languages have individual precedence and associativity rules – C has 15 levels – too many to remember – Pascal has 3 levels – too few for good semantics – Fortran has 8 – Ada has 6 – when unsure, use parentheses

Figure 6.1 Operator precedence levels in Fortran, Pascal, C, and Ada. The operator s at the top of the figure group most tightly.

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

2 9/15/2020

Expression Evaluation Assignment • example: • typically, a variable takes on a new value – Fortran: 3 + 2**2**3 • assignment is a side effect (or, something • exponentiation has higher precedence than that influences later computation or output • exponentiation has right to left associativity and is not a return value) – use parentheses to force other interpretations • 3 + 2**(2**3) – C: assignment does yield a value • (3 + 2)**2**3 • l-value: term on left side of = • r-value: term on right side of =

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Assignment Expression Evaluation • ordering of operand evaluation (generally • short-circuiting none) – consider (a < b) && (b < c): • If a >= b there is no point evaluating whether b < c • application of arithmetic identities because (a < b) && (b < c) is automatically false – commutativity is assumed to be safe – other similar situations – associativity (known to be dangerous) if (b != 0 && a/b == c) ... if (*p && p->foo) ... a + (b + c) works if a~=maxint and b~=minint and c<0 if (unlikely_condition && (a + b) + c does not very_expensive function()) ... • this type of operation can be useful, though, for code • be cautious - need to be sure that your second half optimization is valid, or else coder could miss a runtime error without proper testing. Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Expression Evaluation Expression versus statements • variables as values vs. variables as references • most languages distinguish between expressions – value-oriented languages and statements. • C, Pascal, Ada – expressions always produce a value, and may or may – reference-oriented languages not have a side effect. • Python: b + c • most functional languages (Lisp, Scheme, ML) – statements are executed solely for their side effects, and • Clu, return no useful value – Algol-68 is halfway in-between • Python: mylist.sort() – Java deliberately in-between • a construct has a side effect if it influences • built-in types are values subsequent computation in some way (other than • user-defined types are objects - references simply returning a value) Copyright © 2009 Elsevier Copyright © 2009 Elsevier

3 9/15/2020

Expression Evaluation Algol 68 • expression-oriented vs. statement-oriented • orthogonality languages – features that can be used in any combination – expression-oriented: – Algol 68: everything is an expression (there is no • functional languages (Lisp, Scheme, ML) separate notion of statements) • Algol-68 • example: – statement-oriented: begin • most imperative languages a := if b

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Assignment shortcuts Multiway Assignment • assignment • some languages (including ML, , – statement (or expression) executed for its side Python and Ruby) allow multiway effect - key to most programming languages you assignment. have seen so far – example: a,b = c,d; – assignment operators (+=, -=, etc) – defines a tuple, equivalent to a = c; b = d; • handy shortcuts • this feature can simplify computation • avoid redundant work (or need for optimization) • perform side effects exactly once – a,b = b,a (no need for an aux variable) – example: A[index_fn(i)]++; – a,b,c = foo(d,e,f) (allows a single return) – vs. A[index_fn(i)] = A[index_fn(i)] + 1;

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

C and assignments within expressions Expression Evaluation • combining expressions with assignments can have • side effects are a fundamental aspect of the unfortunate side effects, depending on the language. whole von Neumann model of – pathological example: C has no true boolean type (just computation. uses int’s or their equivalents), and allows assignments – what is the von Neumann architecture? within expressions. • in (pure) functional, logic, and dataflow – example: • if (a = b) { languages, there are no such changes … – single-assignment languages } – very different What does this do?

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

4 9/15/2020

Expression Evaluation More on Side Effects

• several languages outlaw side effects for • side effects are a particular problem if they affect functions state used in other parts of the expression in which a – easier to prove things about programs function call appears – closer to Mathematical intuition – example: a - f(b) - c*d OK? – easier to optimize – good not to specify an order, because it makes it easier to optimize – (often) easier to understand – Fortran allows side effects • but side effects can be nice • but they are not allowed to change other parts of the expression – consider rand() containing the function call • unfortunately, compilers can' check this completely, and most don't at all

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Code optimization Sequencing

• most compilers attempt to optimize code • sequencing – example: a = b + c, then d = c + e + b – specifies a linear ordering on statements • evaluating part of each statement can speed up code • one statement follows another – a = b / c / d then e = f / d / c – very imperative, Von-Neuman – t = c * d and then a = b / t and e = f / t • in assembly, the only way to “jump” around is • arithmetic overflow can really become a problem here to use branch statements. – can be dependent on implementation and local setup • early programming languages mimicked this, – checking provides more work for compiler, so slower such as Fortran (and even Basic and C) – with no checks, these can be hard to find Copyright © 2009 Elsevier Copyright © 2009 Elsevier

The End of Alternatives to goto • in 1968, Edsger Dijkstra wrote an article • getting rid of goto was actually fairly easy, condemning the goto statement since it was usually used in certain ways • while hotly debated, goto’s have essentially – goto to jump to end of current : use disappeared from modern programming language return instead • : a model which took off in – goto to escape from the middle of a loop: use exit the 1970’s or break – top down design – goto to repeat sections of code: loops – modularization of code – structured types – descriptive variables Copyright © 2009– Elsevieriteration Copyright © 2009 Elsevier

5 9/15/2020

Biggest need for goto Biggest Need for goto • several settings are very useful for , • another example: exceptions however • goto was generally used as error handling, to – to end a procedure/loop early (for example, if exit a section of code without continuing target value is found). • modern languages generally throw and catch • solution: break or continue exceptions instead – problem: bookkeeping • breaking out of code might end a - need to call destructors, – adds overhead deallocate variables, etc. – but allows more graceful recovery if a section of • adds overhead to stack control - must be support for “unwinding code is unable to fulfill its contract the stack”

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Sequencing Selection • blocks of code are executed in a sequence • selection: introduced in Algol 60 • blocks are generally indicated by { … } or similar construct • interesting note: without side effects (as in Agol 68), blocks – sequential if statements are essentially useless - the value is just the last return if ... then ... else • in other languages, such as Euclid and Turing, functions if ... then ... elsif ... else which return a value are not allowed to have a side effect at – Lisp variant: all (cond – main advantage: these are idempotent - any function call will (C1) (E1) have the same value, no matter when it occurs (C2) (E2) • clearly, that is not always desirable, of course ... – rand function definitely should not return the same thing every time! (Cn) (En) (T) (Et) Copyright © 2009 Elsevier Copyright © 2009 Elsevier )

Selection Selection

• Algol 60 example • selection if a = b then PROC := 2 – Fortran computed gotos elsif a = c then PROC := 3 elsif a = d then PROC := 4 – jump code else PROC := 1 • for selection and logically-controlled loops end; • no point in computing a Boolean value into a register, then • Lisp variant testing it (cond ((= A B) (2)) • instead of passing register containing Boolean out of ((= A C) (3)) expression as a synthesized attribute, pass inherited ((= A D) (4)) attributes INTO expression indicating where to jump to if (T (1)) true, and where to jump to if false ) Copyright © 2009 Elsevier Copyright © 2009 Elsevier

6 9/15/2020

Selection Selection • jump is especially useful in the presence of short- • code generated w/o short-circuiting (Pascal) circuiting r1 := A -- load r2 := B • example: suppose code is generated for the r1 := r1 > r2 r2 := C following: r3 := D r2 := r2 > r3 r1 := r1 & r2 r2 := E if ((A > B) and (C > D)) or (E <> F) then r3 := F r2 := r2 <> r3 then_clause r1 := r1 | r2 if r1 = 0 goto L2 else L1: then_clause -- not actually used else_clause goto L3 L2: else_clause L3:

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Selection Selection: case/switch • code generated w/ short-circuiting (C) • the case/ was introduced in Algol W to simplify certain if-else situations r1 := A r2 := B • useful when comparing the same integer to a large variety if r1 <= r2 goto L4 of possibilities r1 := C r2 := D if r1 > r2 goto L1 L4: r1 := E r2 := F if r1 = r2 goto L2 L1: then_clause goto L3 L2: else_clause L3:

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Selection: case/switch Selection: case/switch • Modula-2 example: • labels and arms must be disjoint • label type must be discrete • integer, character, enumeration, subrange • case/switch statements enhance code aesthetics, but principal motivation is to generate efficient target code

can be re-written as

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

7 9/15/2020

Selection: case/switch Selection: case/switch • case can be translated as • can use an array of jump addresses (jump table) instead

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Selection: case/switch Selection: case/switch • jump tables can take a lot of space if case covers large • languages differ in ranges or values or non-dense • syntax • alternative methods • punctuation • sequential testing: useful if number of case statements is small • label ranges • hashing: useful if range of label values is large, but with many • default clause missing values • Modula: else • binary search: good for large ranges • Ada: all values must be covered • handling of match failures • some languages will require program failure for unmatched value • C and Fortran 90: no effect

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Selection: case/switch Selection: case/switch • C/C++/Java switch • C/C++/Java switch • each value must have its own label; no ranges allowed • lists of labels not allowed, but empty arms that fall through OK • break required at end of each arm that terminates

• fall-through can cause unintentional hard-to-find bugs • C# requires each non-empty arm to end with break, goto, continue, or return • fall-through convenient at times

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

8 9/15/2020

Iteration Iteration • ability to perform some set of operations repeatedly • enumeration-controlled loop – loops – originated in Fortran – recursion – Pascal or Fortran-style for loops • without iteration, all code would run in linear time do i = 1, 10, 2 -- index i, init val, bound, step … -- body will execute 5 times • most powerful component of programming enddo

• in general, loops are more common in imperative – changed to standard for loops later, eg Modula-2 languages, while recursion is more common in FOR i := first TO last BY step DO functional languages … – loops generally executed for their side effects END

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Iteration: Code Generation Iteration: Code Generation • none of these initial loops allow anything other than • translation can be optimized if the number of iterations can enumeration over a preset, fixed number of values be precomputed, although need to be careful of overflow • results in efficient code generation – precompute total count, and subtract 1 each time until we hit 0 R1 := first – often used in early Fortran compilers R2 := step – we must be able to precompute R3 := last • always possible in Fortran or Ada, but C (and its descendants) are quite different. goto L2

L1: … --loop body, use R1 for i R1 := R1 + R2 L2: if R1 <= R3 goto L1

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Iteration: Some Issues Iteration: Some Issues

• can control enter or leave the loop other than through enumeration • example: what happens if the loop modifies the index variable itself?

mechanism? for i := 1 to 10 by 2 – break, continue, exit … – Fortran allowed goto to jump inside a loop if i = 3 • what happens if the loop body alters variables used to compute end-of- i = 6 loop condition? – some languages only compute the bound once (not C) • example: can the program read the index after the loop has been • what happens if the loop modifies the index variable itself? completed, and if so, what is its value? – most languages prohibit this entirely, although some leave it up to the programmer • can the program read the index after the loop has been completed, and if so, what is its value? – ties into issue of scope, and is very language-dependent

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

9 9/15/2020

Iteration: Combination Loops Iteration: Combination Loops

• the in C is called a combination loop - it allows one to use • for loop useful in its compactness of clarity over more complex structures in the for loop • convenient to make loop iterator local to body of loop • the Modula-2 loop for (int i = first; i <= last; i += step) • essentially, for loops are another variant of while loops, with more complex updates and true/false evaluations each time becomes • operator overloading (such as operator++) combined with iterators actually allow highly non-enumerative for loops

• example:

for (list::iterator it = mylist.begin(); which is equivalent to it != mylist.end(); it++) { … }

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Iteration: Iterators Iteration: Logically Controlled Loops • languages such as Ruby, Python, and C# require any • while loops are different than the standard, Fortran-style container to provide an iterator that enumerates items in for loops, since no set number of enumerations is that class predefined • extremely high-level, and relatively new • these are inherently strong - closer to if statements, in • example some ways, but with repetition built in also for item in mylist: • down side: much more difficult to code properly, and more #code to look at items difficult to debug • code optimization is also (in some sense) harder - none of the for loop tricks will work

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Recursion Recursion: Slower? • recursion • many criticize that recursion is slower and less – equally powerful to iteration efficient than iteration, since you have to alter the stack when calling a function – mechanical transformations back and forth • a bit inaccurate – naively written iteration is probably – often more intuitive (sometimes less) more efficient than naively written recursion – naïve implementation less efficient • in particular, if the recursion is tail recursion, the • no special syntax required execution on the stack for the recursive call will • fundamental to functional languages like Scheme occupy the exact same spot as the previous method

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

10 9/15/2020

Recursion Recursion:

• tail recursion • even if not initially tail recursive, simple – no computation follows recursive call transformations can often produce tail-recursive code int gcd (int a, int b) { • -passing (more in a later chapter) /* assume a, b > 0 */ if (a == b) return a; • additionally, clever tricks - such as computing else if (a > b) return gcd (a - b, b) Fibonacci numbers in an increasing fashion, rather else return gcd (a, b – a); than via two recursive calls - can make recursion } – a good compiler will translate this to that comparable runs “in place”, essentially returning to the start of the function with new a,b values

Copyright © 2009 Elsevier Copyright © 2009 Elsevier

Order of Evaluation

• generally, we assume that arguments are evaluated before passing to a subroutine, in applicative order evaluations • not always the case: or normal order evaluation pass unevaluated arguments to functions, and value is only computed if and when it is necessary • applicative order is preferable for clarity and efficiency, but sometimes normal order can lead to faster code or code that won’t give as many run-time errors • in particular, for list-type structures in functional languages, this lazy evaluation can be key

Copyright © 2009 Elsevier

11