220 likes | 413 Views
Control Structures. Any mechanism that departs from straight-line execution: Selection: if-statements Multiway-selection: case statements Unbounded iteration: while-loops Definite iteration: for-loops Iterations over collections transfer of control: gotos
E N D
Control Structures • Any mechanism that departs from straight-line execution: • Selection: if-statements • Multiway-selection: case statements • Unbounded iteration: while-loops • Definite iteration: for-loops • Iterations over collections • transfer of control: gotos • unbounded transfer of control: exceptions, backtracking
Selection • if Condition then Statement -- Pascal, Ada • if (Condition) Statement -- C, C++ Java • All you need for a universal machine: increment, decrement, branch on zero. All the rest is programmer convenience! • To avoid ambiguities, use end marker: end if, “}” • To deal with alternatives, use keyword or bracketing: if Condition then if (Condition) { Statements Statements } elsif Condition then else if (Condition) { Statements Statements} elseelse { Statements Statements} end if;
Nesting if Condition thenif (Condition) if Condition thenif (Condition) { Statements Statements end if; } else else { Statements Statements end if; }
Statement Grouping • Pascal introduces begin-end pair to mark sequence • C/C++/Java abbreviate keywords to { } • Ada dispenses with brackets for sequences, because keywords for the enclosing control structure are sufficient: • for J in 1 .. N loop … end loop; • More writing => more readable • The use of grouping in C a reminder that it is an expression language • The use of grouping in C++/Java is just syntactic tradition • Another possibility (ABC, Python): make indentation significant
Short-Circuit Evaluation • If x is more than five times greater than y, compute z: • if x / y > 5 then z := … -- but what if y is 0? • If y /= 0 and x/ y > 5then z := … -- but operators evaluate their arguments • Solutions: • a lazy evaluation rule for logical operators (LISP, C, etc) • a control structure with a different syntax • C1 && C2 does not evaluate C2 if C1 is false • if C1 andthen C2 then ditto • C1 || C2 does not evaluate C2 if C1 is true • if C1 or else C2 then ditto
Multiway selection • The case statement is the most useful control structure because most programs are interpreters. • Can be simulated with a sequence of if-statements, but logic • become obscured. case Next_Char is when ‘I’ => Val := 1; when ‘V’ => Val := 5; when ‘X’ => Val := 10; when ‘C’ => Val := 100; when ‘D’ => Val := 500; when ‘M’ => Val := 1000; when others => raise Illegal_Numeral; end case;
The well-structured case statement • Type of expression must be discrete: an enumerable set of values (floating-point numbers not acceptable) • each choice is independent of the others (no flow-through) • There are no duplicate choices • All possible choices are covered • There is mechanism to specify a default outcome for choices not given explicitly.
Implementation • Finite set of possibilities: can build a table of addresses, and • convert expression into table index: • compute value • transform into index • retrieve address of corresponding code fragment • branch to code fragment and execute • branch to end of case statement • All cases have the same execution cost • All choices must be static: computable at compile-time
Complications case (X + 1) is -- any integer value (discrete but large) when integer’first .. 0 => Put_Line (“negative”); when 1 => Put_Line (“unit”); when 3 | 5 | 7 | 11 => Put_Line (“smal prime”); when 2 | 4 | 6 | 8 | 10 => Put_Line (“small even”); when 21 => Put_Line (“the house wins”); when 12 .. 20 | 22 .. 99 => Put_Line (“manageable”); when others => Put_Line (“Irrelevant”); end case; • Implementation must be combination of tables and if-statements.
Unstructured flow (Duff’s device) void send (int* to, int* from, int count) { int n = (count + 7 ) / 8; switch (count % 8) { case 0 : do { *to++ = *from++; case 7 : *to++ = *from++; case 6 : *to++ = *from++; case 5 : *to++ = *from++; case 4 : *to++ = *from++; case 3 : *to++ = *from++; case 2 : *to++ = *from++; case 1 : *to++ = *from++; } while (--n >0); } } What does this do? Why bother?
Indefinite loops • All loops can be expressed as while-loops • Condition is evaluated at each iteration • If condition is initially false, loop is never executed while Condition loop .. end loop; • equivalent to if Condition then while Condition loop … end loop; end if; • (provided Condition has no side-effects…)
What if we want to execute at least once? • Pascal introduces until-loop. • C/C++ use different syntax with while: while (Condition) { … } do { … } while (Condition) • While form is most common • Can always simulate with a boolean variable: first := True; while (Condition or else first) loop … first := False; end loop;
Breaking out • More common is the need for an indefinite loop that terminates in the middle of an iteration. • C/C++/Java: break • Ada : exit statement loop-- infinite loop compute_first_part; exit when got_it; compute_some_more; end loop;
Multiple exits • Within nested loops, useful to specify exit from several of them • Ada solution: give names to loops • Otherwise: use a counter (Modula) or use a goto. • Outer: while C1 loop ... • Inner: while C2 loop... • Innermost: while C3 loop... • exit Outer when Major_Failure; • exit Inner when Small_Annoyance; • ... • end loop Innermost; • end loop Inner; • end loop Outer;
Definite loops • Counting loops are iterators over discrete domains: • for J in 1..10 loop … • for (int I = 0; I < N; I++ ) .. • Design issues: • Evaluation of bounds (only once, ever since Algol60) • Scope of loop variable • Empty loops • Increments other than one • Backwards iteration • non-numeric domains
Evaluation of bounds for J in 1 .. N loop … N := N + 1; end loop; -- terminates? • In Ada, bounds are evaluated once before iteration starts. The above always terminates (and is abominable style). • The C/C++/Java loop has hybrid semantics: for (int J = 0; J < last; J++) { … last++; -- does not terminate! }
The loop variable • Best if local to loop and treated as constant • Avoids issue of value at termination, and value on abrupt exit • counter : integer := 17; -- outer declaration • ... • for counter in 1 ..10 loop • do_something; -- 1 <= counter <= 10 • end loop; • … • -- counter is still 17
Different increments • The universal Algol60 form: • for J from Exp1 to Exp2 by Exp3 do… • Too rich for most cases. Exp3 is most often +1, -1. • What is meaning if Exp1 > Exp2 and Exp3 < 0 ? • In C/ C++ for (int J = Exp1; J <= Exp2; J = J + Exp3) … • In Ada: for J in 1 .. N loop-- increment is +1 for J in reverse 1 .. N loop-- increment is -1 • Everything else can be programmed with while-loop
Non-numeric domains • Ada form generalizes to discrete types: • for M in months loop … • Basic pattern on other data-types: • define primitive operations: first, next, more_elements: Iterator = Collection.Iterate(); // build an iterator over a collection of elements element thing = iterator.first; // define loop variable while (iterator.more_elements ()) { ... thing = iterator.next (); // value is each successive element }
How do we know it’s right? • Pre-Conditions and Post-conditions: • {P} S {Q} means: • if Proposition P holds before executing S, • and the execution of S terminates, • then proposition Q holds afterwards. • Need to formulate pre- and post-conditions for all statement forms, and syntax-directed rules of inference. {P and C} S {P} (P and C} while C do S end loop; {P and not C} i.e. on exit from a while-loop we know the condition is false.
Efficient exponentiation function Exp (Base : Integer; Expon : Integer) return integer is N : Integer := Expon; -- to pick up successive bits of exponent Res : Integer := 1; -- running result Pow : Integer := Base; -- successive powers: Base ** (2 ** I) begin while N > 0 loop if N mod 2 = 1 then Res := Res * Pow; end if; Pow := Pow * Pow; N := N / 2; end loop; return Res; end Exp;
Adding invariants function Exp (Base : Integer; Expon : Integer) return integer is … declarations … {i = 0} to count iterations begin while N > 0 loop {i := i + 1} if N mod 2 = 1 then { N mod 2 is ith bit of Expon from left} Res := Res * Pow; { Res := Base ** (Expon mod 2 ** i} end if; Pow := Pow * Pow; { Pow := Base ** (2 ** i)} N := N / 2; { N := Expon / ( 2 ** i) } end loop; return Res; {i = log Expon; N = 0; Res = Base ** Expon} end Exp;