Control statements in imperative languages mirror the instruction-set hardware
// unconditional branches in C: (see code) j = 0; while (j < 3) { if (array[j] < 0) goto out_of_bounds; if (array[j] == 99) break; // breaks to first line after loop if (array[j] > 1) { j++; continue; // takes you to the top of the loop } } printf("break got me here\n"); cleanup(); out_of_bounds: printf("value %d got me here\n",array[j]);
for (i = 0; i < SIZE; i++)
{
if (num[i] <= 1.00)
continue; // skip values <= 1.00
printf("value = %.2f\n", num[i]);
}
for (i = 0; i < SIZE; i++)
{
if (num[i] <= 1.00)
break; // exit loop completely for values <= 1.00
printf("value = %.2f\n", num[i]);
}
! Example of spaghetti code in Fortran
GOTO 10 ! jump forward
23 CONTINUE
i = i - 1
IF (i .eq. 0) GOTO 99
10 PRINT "I Loop"
69 j = j - 1 ! loop
print "J Loop"
IF (j .ne. 0) GOTO 69
GOTO 23 ! jump back
099 CONTINUE
// the same code without gotos is cleaner
do {
print "I Loop"
while (j>0) {
print "J Loop"
j--
}
i--
}
while (i > 0)
// try converting this into a loop without GOTOs or unconditional jumps
L1: if (cond1) then goto L2
statement1;
if (cond2) then goto L2
statement2;
goto L1;
L2: statement3;
If the last thing a function does before the return is call itself, the function is tail recursive.
// tail recursive - all the work is done on the way *up* the stack
int sum = 0;
void foo (int i, int list[]) {
if (i == size)
return;
sum += list[i++];
foo (i, list); // last thing done is the recursive call
}
// NOT tail recursive - all the work is done on the way *down* while
// popping the call frames
int fac (int n) {
if (n == 0) return 1
else
return n * fac(n-1); // last operation is mult
}
// tail recursive function
void stuff (int n) {
print n;
if (n>0)
stuff (n-1);
}
// converted into an equivalent loop
while (n > 0) {
print n;
n--;
}
void treeprint(Node * t){
if (t != null) {
treeprint(t->left);
print(t);
treeprint(t->right);
}
}
First replace the tail recursive call with a goto:
void treeprint(Node * t){
L: if (t != null) {
treeprint(t->left);
print(t);
t = t->right; <= normally done in the recursive call
goto L;
}
}
The remaining recursive call can then be eliminated with a stack:
void treeprint(Node * t){
Stack s; <= holds pointers
L1: while ( t != null ) {
s.push(t);
p = t->left;
goto L1;
L2: s.pop(t);
print(t->value);
t = t->right;
} // end while
if (!s.empty())
goto L2;
}
Example 6c.
All selection statements have a condition:
if (!EOF)
read(ch); // executed when EOF is false
One-Way Selection Statements
if (condition)
statement(s);
else
statement(s);
The dangling else problem:
if (sum == 0)
if (count == 0)
result = 0;
else result = 1; // which 'if' gets the 'else'?
In Perl all then/else clauses are compound (solves dangling else)
To force alternative semantics use compound statements:
if (sum == 0) {
if (count == 0)
result = 0;
}
else result = 1; Multiple-Way Selection Statements
switch (k) { // must be integral type
case 2:
Console.Write("\nm is 1 or 2\n", k);
j = 2*k-1;
break; //explicit break is required - no fall through
case 5:
Console.Write("\nm is 3 or 5\n", k);
j = 3*k+1;
break;
case 4:
Console.Write("\nm is 4\n", k);
j = 4*k-1;
break;
case 1:
// no fall through allowed in C#
break;
default: // supported but not required
Console.Write ("\neverything else, m =%d\n", k);
j = k-2;
break;
}
// syntax of C's switch statement
switch (expression) {
case const_expr_1: stmt_1;
...
case const_expr_n: stmt_n;
[default: stmt_n+1]
}
Ada's "No Fall Through" is more reliable than C - once stmt_seq is completed, control is passed out
case expression is
when choice list => stmt_sequence;
...
when choice list => stmt_sequence;
when others => stmt_sequence;]
end case;
next_stmt; // control passed here
Multiple-Way Selection with elsif
Unlike C, some languages include an explicit keyword for "else if" such as elsif or elif.
In Python:
if x < 0:
x = 0
print 'Negative changed to zero'
elif x == 0:
print 'Zero'
elif x == 1:
print 'Single'
if ...
then ...
elsif ...
then ...
elsif ...
then ...
else ...
end if
if (num < 1.0) {
print "1";
}
elsif ( num < 2.0) {
print "2";
}
elsif ( num < 3.0) {
print "3";
}
General design issues for iteration control statements:
#1 How is iteration controlled?
#2 Where is the control mechanism in the loop?
Design Issues:
Example, FORTRAN 90:
DO label var = start, finish [, stepsize]
1. Loop variable must be INTEGER type
2. Loop variable always has a last value
3. The loop variable cannot be changed in the loop
4. Loop parameters are evaluated only once and can be changed
after evaluation w/o affecting loop execution
5. Stepwize can be any value but zero
6. Parameters can be expressions
Example, Pascal:
for variable := initial (to|downto) final do statement
1. Loop variable must be an ordinal type of usual scope
2. After normal termination, loop variable is undefined
3. The loop variable cannot be changed in the loop
4. The loop parameters can be changed, but they are evaluated just once, so
it does not affect loop control
Example, Ada:
for var in [reverse] discrete_range loop
...
end loop
1. A discrete range is a sub-range of an integer or enumeration type
2. Scope of the loop variable is the range of the loop
3. Loop variable is implicitly undeclared after loop termination
Example: C's for loop is LISP-like
for ([expr_1] ; [expr_2] ; [expr_3]) statement
which is equivalent to:
expr_1;
while ( expr_2 ) {
statement
expr_3;
} Java and C# force the exclusive use of Boolean expressions in control expressions.
upside is security:
while (i = 1) // prevents this error since 'i' is INT type
downside is a lack of flexibility:
int num = 5; // you want num of type int to control while
while (num){ // exit loop when num is 0
x / num; // a divide by zero error will never occur
num--;
}
while (ctrl_expr) // pre-test do loop body do loop body while (ctrl_expr) // post-test
for (p=root; p==NULL; traverse(p))
{
. . .
}
# Perl foreach(@words) { $word = $_; $word =~ tr/A-Z/a-z/; print 'word: ', $_, "\n"; }
Perl's while (<FILEHANDLE>) iterates on the lines of a file.// C# Strings[ ] = strList = {.Bob., .Carol., .Ted.}; foreach (Strings name in strList) Console.WriteLine (.Name: {0}., name);
open(WORDS, $filename); while(<WORDS>) { . . . } close(WORDS);
if <Boolean exp> -> <statement> do this [ ] <Boolean exp> -> <statement> OR do this ... [ ] <Boolean exp> -> <statement> OR do this fi
LOOP Guarded Command:
do <Boolean> -> <statement> [ ] <Boolean> -> <statement> ... [ ] <Boolean> -> <statement> od
if a >= b then print "More or equal";
else print "Less";
Guarded:
if a >= b => print "More or equal"
[ ] a < b => print "Less"
fi
if
a >= b -> print "More or equal"
a <= b -> print "Less or equal"
fi
When a = b, the result of command can be "More or equal" or "Less or equal".
Guarded commands are a means for controlling asynchronous message passing - allows all requestors an equal but nondeterministic chance of communicating ( see Ada code )
Ada has concurrency built-in. A task is the basic unit of concurrency. Concurrent hello world
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Numerics.Float_Random; use Ada.Numerics.Float_Random;
procedure Concurrent_Hello is
task type Writer (Message : access String);
task body Writer is
Seed : Generator;
begin
Reset (Seed);
delay Duration (Random (Seed));
Put_Line (Message.all);
end Writer;
S1 : aliased String := "Enjoy";
S2 : aliased String := "Rosetta";
S3 : aliased String := "Code";
T1 : Writer (S1'Access);
T2 : Writer (S2'Access);
T3 : Writer (S3'Access);
begin
null;
end Concurrent_Hello;
IBM's latest OO/concurrent programming language for high-performance computing (e.g. Watson) X10
TOP