650 likes | 961 Views
Iteration and Recursion . Programming Language Principles Lecture 21. Prepared by Manuel E. Bermúdez, Ph.D. Associate Professor University of Florida. Iteration. Execute a block multiple times for its side effects. Enumeration controlled loops:
E N D
Iteration and Recursion Programming Language Principles Lecture 21 Prepared by Manuel E. Bermúdez, Ph.D. Associate Professor University of Florida
Iteration • Execute a block multiple times for its side effects. • Enumeration controlled loops: • Execute a block for every value in a finite set. • Fortran: do 10 i=1,10,2 ... 10 continue :
Classic Problems in Fortran • 'do' as prefix of assignment: do7i=j+3*4,20 do loop do7i=j+3+4 assignment 'do7i' is a valid identifier. 'do' not a reserved word in Fortran. Need to look ahead arbitrary distance, to the 'comma'.
Classic Problems in Fortran (cont’d) • Loop body can change the loop index: do 10 i=2,20 i=i-1 10 continue infinite loop • Goto's can jump into the middle of a loop. • So what's the value of the index, in that case ??
Classic Problems in Fortran (cont’d) • If goto branches out of loop, value of i is last assigned. If loop terminates normally, value of i is implementation-defined. • Last increment of i can cause overflow: negative value ??? • Fortran loop is bottom-tested: loop body will execute at least once.
Pascal • (and Modula, and most languages with enumeration controlled loops): for i := first to last by step do begin ... end;
Pascal (cont’d) • Questions: • Can i, first or last be modified in the loop? • If so, what is the effect? • What if first > last? • What 's the value of i when done? • Can one jump from outside, into the loop? • What if first has a side effect on i, or on last, or vice-versa?
Pascal (cont’d) • Most languages prohibit modifying the loop index. • Very expensive to check at compile time. Need to check for: • Assignments to i • Nested loops that use i. • Passing i to a procedure by reference. • Reading i.
Pascal (cont’d) • What does this Pascal for statement do? i := 7; for i := i+1 to i+8 do begin ... end; • What if instead of i+1, we had f(i), where f takes i as a reference parameter ?
Modern For Loops Are Top-Tested (cont’d) • Works only for positive steps. • In Pascal, for i := 10 down to 1 do ... • In Ada, for i in reverse 1..10 do ... • In Modula, FOR i := 10 to 1 STEP -1 compile-time constant
Modern For Loops Are Top-Tested (cont’d) • In Fortran • No "backward" syntax, • Compile-time constant steps not required. • Can use an "iteration count“
Modern For Loops Are Top-Tested (cont’d) • In C, C++, Java, it's simple. No loop index. for (e1; e2; e3) body; Code is: e1; L2: if not e2 goto L1 body e3; <--- any 'continue' branches here goto L2 L1: ...
Modern For Loops Are Top-Tested (cont’d) • NOT equivalent to: e1; while (e2) { body; /* if this contains a 'continue', */ e3; /* e3 is not executed */ }
Modern For Loops Are Top-Tested (cont’d) • In C, programmer's responsibility: • effect of overflow. • index, other variables, can be modified inside loop. • e1, e2, e3 are all optional. If e2 is missing, it's considered to be a 1 (true).
Access to the Index Outside the Loop • Fortran IV, Pascal, leave the loop index undefined. • Fortran 77, Algol 60, leave the "last value assigned". • In Pascal, var c: 'a' .. 'z'; for c := 'a' to 'z' do begin ... end; (* what's the value of c ? *)
Access to the Index Outside the Loop (cont’d) • Compiler forced to generate slower code • Two branches in each iteration
Access to the Index Outside the Loop (cont’d) • Several languages (Algol W, Algol 68, Ada, Modula-3, new ISO C++): • loop header *declares* loop index. • loop index's type is inferred from loop bounds. • not visible outside the loop.
Combination Loops • Algol 60 allows a 'for-list' of enumerated values/ranges. • Syntax: Stmt 'for' id ':=‘ Enumerator list ',' 'do' Stmt Enumerator Expr Expr 'step' Expr 'until' Expr Expr while Condition
Combination Loops (cont’d) • Examples (all three are equivalent): for i := 1, 3, 5, 9 do ... for i := 1 step 2 until 10 do ... for i := 1, i + 2 while i < 10 do ...
Iterators • An iterator allows examination of the elements in a data structure, one at a time. • Various kinds of iterators available in Clu, Icon (see textbook). • Java has a built-in iterator class.
Iterator Methods in Java Iterator ix = x.iterator(); • Constructs and initializes an iterator for the elements of x. • ix is the new iterator. • The class for x must define the iterator() method.
Iterator Methods in Java (cont’d) ix.hasNext() • Returns true iff x has a next element. ix.next() • Return next element; • Throws NoSuchElementException if there is no next element.
Iterator Methods in Java (cont’d) ix.remove() • Removes last element returned by ix.next(). • Throws UnsupportedMethodEXception if method not implemented. • Throws IllegalStateException if ix.next() not yet called or did not return an element.
Using the Iterator Iterator ix = x.iterator(); while (ix.hasNext()) examine(ix.next()); • Much better than for (int i=0; i<x.size(); i++) examine (x.get(i));
Advantages of Iterators • More abstraction. Object class "knows" how to iterate. • Often possible to implement next() more efficiently than get(). Example: linked list. • Data structures often have no get-by-index method.
"Faking" an Iterator in C • Using a "fake" iterator in C: • C code for the binary tree pre-order traversal iterator (see textbook).
Logically Controlled Loops • Semantically less complex (fewer subtleties). • Execute statement (or block) until a condition becomes true/false (while) • Variations (syntactic sugar of each other):
Logically Controlled Loops (cont’d) while condition do block;(Pascal, Modula) while (condition) block;(C, C++, Java) for i := irrelevant_expression while condition do statement(Algol) 10 if negated-condition goto 20 block goto 10 20 (Fortran)
Logically Controlled Loops (cont’d) • Post-tested loops (execute at least once): repeat statements until condition (Pascal) do statement while condition (C, C++, Java)
Mid-Tested Loops (Quit Anytime): loop statement_list when condition exit statement_list when condition exit ... end(Modula-1) • 'exit' is built in, along with 'when', so exiting from a nested construct is impossible.
Modula-2 • Favored a simple EXIT Statement LOOP line = ReadLine; IF AllBlanks(line) THEN EXIT END; ConsumeLine(line) END; • EXIT statements are only allowed to appear inside LOOPs. • Difficult to enforce.
Modula-3 • EXIT can abort a WHILE, REPEAT, or FOR loop. C: for (;;) { line = read_line(stdin); if (all_blanks(line)) break; consume_line (line); }
C Loops • [M. Scott says "for some reason, for(;;) has traditionally been favored over the equivalent while (1)". • The actual original reason was efficiency, not style: • older compilers would actually test the 1 each time around the while loop. • With today's compilers, it should make no difference.
Mid-Tested Loops (cont’d): • Euclid, Ada: loop ... exit when condition; end loop;
Mid-Tested Loops (cont’d): • Java: • loops can be labeled, • 'break' statement has optional label. outer: while (true) { get_line(line); for (i=1; i<=length; i++) if line[i]='#' break outer; consume_line(line); }
Recursion • Any iterative formulation can be expressed as recursion, and vice versa. They are equally powerful. • Some functional languages do not allow iteration. • Iteration often more efficiently implemented.
Recursion (cont’d) • Optimizing compilers for functional languages usually generate very good code. • The use of iteration/recursion is mostly a matter of ease of use: • Iteration more efficient than recursion ? • Naive implementations are.
Recursion (cont’d) • For some problems, iteration seems natural. for (i=low; i<=high; i++) total += f(i); • For other problems, recursion seems more natural: let gcd a b = a eq b -> a | a < b -> gcd a (b-a) | gcd (a-b) b
Recursion (cont’d) • In C, int gcd (int a, int b) { if (a==b) return a; else if (a > b) return gcd(a-b,b); else return gcd(a, b-a); } • In both cases, the choice could go the other way.
Recursion (cont’d) • Summation in C, recursive: typedef int (*int_func) int; int summation (int_func f, int low, int high) { if (low == high) return f (low) else return f(low) + summation(f, low+1,high); }
Recursion (cont’d) • GCD in C, non-recursive: int gcd(int a,int b) { while(a!=b) if (a > b) a = a - b; else b = b - a; return a; }
Tail-Recursion • Additional computation never follows a recursive call. (nothing done as recursion unwinds). • No need to allocate stack space dynamically: we can reuse previous space. • Continuation-passing style: • can always avoid doing work after recursive call by passing that work into the recursive call, a continuation.
Tail-Recursion (cont’d) • Example (RPAL): let rec f n = n eq 1 -> 1 | n * f (n-1) in f 3 let f n = rf n 1 where rec rf n r = n eq 1 -> r | rf (n-1) (n*r) in f 3
Tail-Recursion (cont’d) • Recursion does not lead to algorithmically inferior programs. • Instead, the style of programming just changes (paradigmatically).
Applicative and Normal-Order Evaluation • Called Normal Order and PL Order in RPAL. • Normal order (passing unevaluated parameters) is used for macros in C.
Applicative and Normal-Order Evaluation (cont’d) • Example: #define DIVIDES(n,a) (!((n) % (a))) /* true iff n % a is zero */ • Preprocessor replaces DIVIDES (x,y+z) (textually!, no evaluation) with (!((x) % (y+z)))
Applicative and Normal-Order Evaluation (cont’d) • Parentheses (n), (a) are crucial. Without them, DIVIDES (x,y+z) is replaced with (!(x % y+z)) which is equivalent to (!((x % y)+z))