Control Structures If-then-else. It is in two forms: if cond then s1 else s2 if cond then s1 evaluate condition. If and only if evaluates to true, then evaluate s1 otherwise evaluate s2 Dangling else problem if c1 then if c2 then s1 else s2 may be intrepreted as if c1 then if c2 then s1 else s2 or if c1 then if c2 then s1 else s2 where the indentation represents the intended interpretation. This ambiguity can be avoided by bracketing syntax: if cond then s1 fi if cond then s1 else s2 fi Using this syntax, the above intended statements can be written as if c1 then if c2 then s1 else s2 fi fi or if c1 then if c2 then s1 fi else s2 fi Another way to avoid ambiguity is to use disambiguating rule: associate else with closest "if" that doesn't have "else". This is used in most programming languages (C, C++ etc) Case Statement switch () { case : case : ... default : } Evaluate to get value v. Evaluate the case that corresponds to v. Restriction: has to be value (and not an expression) of an orinal type e.g., int, enum Implementation of case statement Naive algorithm: Sequential comparison of value v with case labels. This is simple, but inefficient. It involves O(N) comparisons. switch (e) { case 0 : s0 ; case 1 : s1 ; case 2 : s2 ; case 3 : s3 ; } can be translated as v = e ; if (v == 0) s0 ; else if (v == 1) s1 ; else if (v == 2) s2 ; else if (v == 3) s3 ; Binary search: O(log N) comparisons, hence a drastic improvement over sequential search for large N. Using this, the above case statement can be translated as v = e ; if (v <= 1) if (v == 0) s0 ; else if (v == 1) s1 ; else (if v >= 2) if (v == 2) s2 ; else if (v == 3) s3 ; Another technique is to use table lookup. The table maps the value v to the case label that corresponds to the value v. Using the table, the branching to the right case body can be effected typically in constant time. NOTE: When we implement the lookup table, we can use array or a hash table. Using array is fast, but may require a lot of unnecessary space if many of the case label values are unused in the case statement. This can happen when the domain of values for the case label is large. Hash tables are somewhat slower than arrays, but space utilization can be kept to a small multiple of the number of cases actually appearing in the case statement. Control Statements: Structured Control Statements: Case Statements: - Implementation using if-then-else - Understand semantics in terms of the semantics of simple constructs - actual implmentation in a compiler Loops - while, repeat, for while: let s1 = while C do S then it can also be written as s1 = if C then {S; s1} repeast let s2 = repeat S until C then it can alos be written as s2 = S; if (!C) then S2 (Note that repeat S until C means execute S; evaluate C and if C is false, repeat this process - until C is true) loop S end (S should contain an exit statement to break out of the loop - otherwise loop will never terminate). NOTE: We can write "while(1)" for an infinite loop. The "loop forever" construct is some times called "syntactic sugar" since it provides a more convenient syntax for something you could already express in the language. We mean that the addition of this construct does not fundamentally alter the expressive power of the language, but is simply a matter of more convenient syntax. Another example of syntactic sugar in C is the "->" operator --- a->b is a more convenient way to express something that could already be expressed in the language using '*' operator, i.e., (*a).b Goto Statements =============== - goto (the only control mechanism in old FORTRAN) - programs with goto's are difficult to understand. Consider 40: if (x > y) then goto 10 if (x < y) then goto 20 goto 30 10: x = x - y goto 40 20: y = y -x goto 40 30: gcd = x an equivalent program with structured control statements is much more readable: while (x != y) do { if (x > y) then x = x - y else y = y - x } goto should be used in rare circumstances; e.g., error handling, when an unrecoverable error condition happens in a deeply nested loop statement Java doesn't have goto; breaking out of deeply nested loops supported via "labelled break" statemnt. e.g., l1 : for ( ... ) { whlie ( ... ) { ... break l1 } } here, break l1 staements causes the control to break out of the for loop Restrictions in use of goto: - jumps outside of a procedure - jumps from outer blocks to inner blocks or unrelated blocks goto l1; if ( ... ) then { int x ; x = 5 ; l1: y = x * x ; } in this case, (if goto is allowed to inner blocks) the value of x is not known when jump is made to l1, since x is allocated only when the block containing it (if statement evaluates to true) is entered, but in this case jump is to middle of the block, so x is not even defined! Similarly, gotos into unrelated blocks is not allowed: if ( ... ) { ... goto l2 ; } else { ... l2 : ... }