CSCI 3136 Principles of Programming Languages Control Flow - 2 Summer 2013 Faculty of Computer Science Dalhousie University 1 / 20
Short-Circuit Evaluation of Boolean Expressions • (and a b) : If a is false, b has no effect on the value of the whole expression. • (or a b) : If a is true, b has no effect on the value of the whole expression. Short-circuit evaluation • If the value of the expression does not depend on b , the evaluation of b is skipped. This is a useful optimization. If the evaluation of b has side-effects, however, the meaning of the code may be changed. Some languages provide both regular and short-circuit versions of Boolean operators. Ada • and vs and then • or vs or else 2 / 20
Short-Circuit Evaluation: Examples • C: (C short-circuits && and || operators) while( p != NULL && p->e != val ) p = p->next; • Pascal: (Pascal does not short-circuit and and or operators ) p := my list; while (p <> nil) and (p^ .key <> val) do p := p^ .next; • Perl: open( F, "file" ) or die; • Replacing if in Perl or shell scripts: if( x > max ) then max = x; becomes ( x > max ) and max = x; 3 / 20
Sequencing In imperative programming languages, sequencing comes naturally, without a need for special syntax to support it. Mixed imperative/functional languages (LISP, Scheme, ...) often provide special constructs for sequencing. Issue : What’s the value of a sequence of expressions/statements? • The value of the last subexpression (most common) C : a = 4, b = 5 LISP : (progn (setq a 4) (setq b 5)) • The value of the first subexpression LISP : (prog1 (setq a 4) (setq b 5)) • The value of the second subexpression (supported in LISP) LISP : (prog2 (setq a 4) (setq b 5) (setq c 6)) 4 / 20
Goto and Alternatives Use of goto is bad programming practice if the same effect can be achieved using different constructs. Sometimes, however, it is unavoidable: • Break out of a loop • Break out of a subroutine • Break out of a deeply nested context Many languages provide alternatives: • One-and-a-half loop • return statement • Structured exception handling 5 / 20
Selection (Alternation) • Standard if-then-else statement if ... then ... else ... • Multi-way if-then-else statement if ... then ... elsif ... then ... elsif ... then ... ... else ... • Switch statement switch ... of case ...: ... case ...: ... ... 6 / 20
Switch Statements Switch statements are a special case of if/then/elsif/else. Principal motivation: Generate more efficient code. Compiler can use different methods to generate efficient code: • Sequential testing • Binary search • Hash table • Jump table 7 / 20
Implementation of Switch Statements (1) i := ... (* expression *) IF i = 1 THEN r1:= ... -- expression clause A if r1 � = 1 goto L1 ELSIF i IN 2, 7 THEN clause A clause B goto L6 ELSIF i IN 3..5 THEN L1: if r1 = 2 goto L2 clause C if r1 � = 7 goto L3 ELSIF i = 10 THEN L2: clause B clause D goto L6 ELSE L3: if r1 < 3 goto L4 clause E if r1 > 5 goto L4 END clause C —————————— goto L6 CASE i OF L4: if r1 � = 10 goto L5 1: clause A clause D | 2, 7: clause B goto L6 | 3..5: clause C L5: clause E | 10: clause D L6: ... ELSE clause E END 8 / 20
Implementation of Switch Statements (2) clause A i := ... (* expression *) L1: Jump table IF i = 1 THEN goto L7 T: &L1 clause A clause B L2: &L2 ELSIF i IN 2, 7 THEN goto L7 &L3 clause B L3: clause C &L3 ELSIF i IN 3..5 THEN goto L7 &L3 clause C L4: clause D &L5 ELSIF i = 10 THEN goto L7 &L2 clause D L5: clause E &L5 ELSE goto L7 &L5 clause E L7: ... &L4 END L6: r1:= ...--expression —————————— if r1 < 1 goto L5 CASE i OF if r1 > 10 goto L5 1: clause A r1 := r1 -1 | 2, 7: clause B r2 := T[r1] | 3..5: clause C goto *r2 | 10: clause D ELSE clause E END 9 / 20
Implementation of Switch Statements (3) Jump table Hash table + Fast: one table lookup to find + Fast: one hash table access to the right branch find the right branch − Potentially large table: one − More complicated entry per possible value − Elements in a range need to be stored individually ⇒ again, Linear search possibly large table − Potentially slow + No storage overhead Binary search ± Fast (but slower than table lookup) + No storage overhead No single implementation is best in all circumstances. Compilers often use different strategies based on the specific code. 10 / 20
Iteration Enumeration-controlled loops • Example: for -loop • One iteration per element in a finite set. • The number of iterations is known in advance. Logically controlled loops • Example: while and repeat loops • Executed until a Boolean condition changes. • The number of iterations is not known in advance. Some languages do not have loop constructs (e.g., Scheme). They use tail recursion instead. 11 / 20
Logically Controlled Loops • Pre-loop test while ... do ... • Post-loop test repeat ... until ... do ... while ... • Mid-loop test or ”one-and-a-half loop” loop ... when ... exit ... when ... exit ... end 12 / 20
Implementation of Iterations (1) for( init ; cond ; step ) { WHILE cond do statements statements } END ——————————— ——————————— evaluate cond L1: r1 = init evaluate cond if not r1 goto L2 L1: r1 = statements if not r1 goto L2 goto L1 statements L2: ... step goto L1 L2: ... 13 / 20
Implementation of Iterations (2) Potentially much more efficient: FOR i = start TO end BY step DO statements END If modifying the loop variable inside If modifying the loop variable inside the loop is allowed: the loop is not allowed: ——————————— ——————————— r1 := start r1 := [( end - start )/ step ]+1 r2 := end L1: if not r1 goto L2 r3 := step statements L1: if r1 > r2 goto L2 decrement r1 statements goto L1 r1 := r1 + r3 L2: ... goto L1 L2: ... 14 / 20
Break and Continue ”Break” statement (”last” in Perl) • Exits the nearest enclosing for , do , while or switch statement. ”Continue” statement (”next” in Perl) • Skips the rest of the current iteration. The loop may have a finally part, which is always executed no matter whether the iteration is executed normally or terminated using a continue or break statement. 15 / 20
Recursion • Recursion replaces iteration in functional programming paradigm. • For example: while( condition ) { S1; S2; ... } • We can use: procedure P() { if( condition ) { S1; S2; ...; P(); } } Iteration is a strictly imperative feature: it relies on updating the iterator variable. 16 / 20
Implementation of Recursion • A naive implementation of recursion is often less efficient than a naive implementation of iteration. • An optimizing compiler often converts recursion into iteration when possible: − Tail recursion: There is no work to be done after the recursive call. − More general recursion: Work to be done after the recursive call may be passed to the recursive call as a continuation. 17 / 20
Tail-recursion • There is no work to be done after the recursive call. • Tail-recursive calls are often optimized into an iterative form, especially in functional languages. • An optimized tail-recursive call does not allocate additional stack space and does not have function call overhead. 18 / 20
Tail-recursion (Example in C) • Consider this recursive definition of the factorial function in C: factorial(n) { if (n == 0) return 1; return n * factorial(n - 1); } • tail-recursive version: factorial1(n, accumulator) { if (n == 0) return accumulator; return factorial1(n - 1, n * accumulator); } factorial(n) { return factorial1(n, 1); } 19 / 20
Tail-recursion (Example in Scheme) • Consider this Scheme function to sum the elements of a list: (define (sum lis) (if (null? lis) 0 (+ (car lis) (sum (cdr lis))))) • tail-recursive version: (define (sum lis) (letrec ((helper (lambda (s lis) (if (null? lis) s (helper (+ s (car lis)) (cdr lis)))))) (helper 0 lis))) 20 / 20
Recommend
More recommend