The Department of Computer Science & Engineering
cse@buffalo

CSE 305
Programming Languages
Lecture Notes
Stuart C. Shapiro


Statement Level Control Structures

According to a theorem proved by Corrado Böhm and Giuseppe Jacopini (CACM, 1966), any procedure can be written with the following control structures:

  1. Sequence: Do one action, and then another, and then another, etc.
  2. Selection: Do one action or another, based on some condition.
  3. Loop: Repeatedly do an action as long as some condition holds.
If these are the only control structures used, some code may need to be repeated. However, if the break (or exit) statement is added, code repetition is not needed.

Note, that in the Preliminaries notes, I said that among the defining criteria of being a programming language was the facilities for sequence, selection, and loop.

Sequence
This is an often overlooked control structure because it is so pervasive.

A standard part of any imperative program is a sequence of statements, to be executed in sequential order.

Fortran indicates statement sequence by line sequence, but also allows a sequence of statements on one line separated by semi-colons (;).

The semi-colon (;) started out as a statement separator, but it turned out to be cleaner syntax to make it a statement terminator, as it is in most current languages.

A compound statement is a sequence of statements treated syntactically as one statement. The statements in a compound statement are usually surrounded by braces ({ ... }), but some languages use begin ... end or other bracketing keywords.

A block is a compound statement in which variables may be declared with scope limited to that compound statement.

Prolog, a logical (relational) programming languages also uses sequence as a basic control structure.

Functional programming languages get sequential execution from the nesting of function calls.

Let's compare sequential execution in:

on the problem of computing the roots of the quadratic equation, ax2 + bx + c, with the constraint that the square root of the discriminant should not be computed more than once.

The Ruby program:

 def quadRoots(a,b,c)
  # Returns an array of the two roots of the quadradic equation, ax^2 + bx + c = 0
  discrim = Math.sqrt(b*b - 4*a*c)
  return (-b + discrim)/(2*a), (-b - discrim)/(2*a)
end

a,b,c = 2,7,5
firstroot, secondroot = quadRoots(a,b,c) # Should be [-1.0, -2.5]
puts "The roots of #{a}x^2 + #{b}x + #{c} = 0 are #{firstroot} and #{secondroot}"
----------------------------------------------------------------------------
<timberlake:Test:1:53> ruby quadRoots.rb
The roots of 2x^2 + 7x + 5 = 0 are -1.0 and -2.5

The Prolog program:

quadRoots(A,B,C,[Firstroot,Secondroot]) :-
	Discrim is sqrt(B*B - 4*A*C),
	Firstroot is (-B + Discrim)/(2*A),
	Secondroot is (-B - Discrim)/(2*A).

:- [A,B,C] = [2,7,5],
	quadRoots(A,B,C,[P,N]),
	format("The roots of ~dx^2 + ~dx + ~d = 0 are ~g and ~g~n", [A,B,C,P,N]),
	halt.
----------------------------------------------------------------------------
<timberlake:Test:1:57> prolog -l quadRoots.pro
% compiling /projects/shapiro/CSE305/Test/quadRoots.pro...
The roots of 2x^2 + 7x + 5 = 0 are -1.0 and -2.5
% compiled /projects/shapiro/CSE305/Test/quadRoots.pro in module user, 10 msec 2016 bytes

The Erlang program:

-module(quadRoots).
-export([main/0,quadRoots/3,printQuadRoots/3]).

discrim(A,B,C) ->
  math:sqrt(B*B - 4*A*C).

twoRoots(A,B,Discrim) ->
  {(-B + Discrim)/(2*A), (-B - Discrim)/(2*A)}.

quadRoots(A,B,C) ->
  % Returns a tuple of the two roots of the quadradic equation, ax^2 + bx + c = 0
  twoRoots(A,B,discrim(A,B,C)).

printRoots(A,B,C,{Firstroot, Secondroot}) ->
  io:format("The roots of ~wx^2 + ~wx + ~w = 0 are ~w and ~w~n",
            [A,B,C,Firstroot,Secondroot]).

printQuadRoots(A,B,C) ->
  printRoots(A,B,C,quadRoots(A,B,C)).

main() ->
  printQuadRoots(2,7,5),
  halt().
----------------------------------------------------------------------------
<timberlake:Test:1:54> erl -compile quadRoots.erl
<timberlake:Test:1:55> erl -noshell -s quadRoots main
The roots of 2x^2 + 7x + 5 = 0 are -1.0 and -2.5

GoTo
The goto is the most basic control structure. It specifies immediate transfer to a given statement. In some languages that statement may be anywhere in the program; other languages have rules that restrict the goto target. The usual syntax is goto <label>, where <label> is a symbolic or numeric label of some statement. (We will assume symbolic labels.) Generally, a statement is labeled using the syntax <label>: <statement>.

Some languages allow a goto target that is the result of a run-time computation.

There are two major objections to the use of goto:

  1. It allows "spaghetti" code that is extremely difficult to understand.
  2. The use of labels makes program verification extremely difficult. Consider the difference between
         x = 3.0;
         y = z / x;
    
    and
               x = 3.0;
         here: y = z / x;
    
    The first obviously does not involve a divide by zero, but consider what you have to do to check that the second does not.

The use of the goto was challenged by Edsger Dijkstra in his famous letter to the editor, "Goto Statement Considered Harmful" (CACM 1968). This launched the structured programming movement. Since the Böhm/Jacopini theorem proved that the goto is not needed, several subsequent languages downplayed, or even eliminated the goto statement. Java, Python, and Ruby, for example, do not have it.

Exit
The exit statement is an executable statement whose effect is to immediately continue execution at the statement immediately following the lexically innermost containing control structure. What control structures may be exited from is language-dependent.

The exit statement can eliminate the need for repeated code when using only the sequence, selection, and loop control structures. For example, consider the following HOSL program for reading and processing some input file:

loop: input := read(file);
      if (input = eof) goto out;
      process(input);
      goto loop;
out:  ...
Using a while, this may be written as:
input := read(file);
while (input != eof) {
    process(input);
    input := read(file);
}
...
Note the repetition of input := read(file);. However, using exit, this may be rewritten as:
while (true) {
    input := read(file);
    if (input == eof) exit;
    process(input);
}
...

Of course, if we can combine reading and testing, this use of exit can be avoided:

while ((input := read(file)) != eof) {
    process(input);
}
or in C,
while (scanf(format, input) != EOF) {
    process(input);
}
Nevertheless, sometimes exit is the only way to avoid repeated code.

A single-level exit always exits from the lexically innermost control structure. A multi-level exit may exit from a more distant lexically containing control structure. In some languages, the multi-level exit takes a numerical parameter, i, and exits from the ith containing structure. In others, the multi-level exit takes a symbolic parameter, label, and exits from the control structure labeled label. This use of a label differs from, and is not as dangerous as the statement label used as the target of a goto. Multi-level exit statements can be used to avoid code repetition that is needed if only the single-level exit is available.

In C-based languages, break is used as the exit statement. In C, C++, Python, and Ruby break is a single-level exit. In Java it is a multi-level exit, taking an optional symbolic label. Perl uses last as a multi-level exit with an optional symbolic label.

In Common Lisp (return-from <label> [<expression>]) is a multi-level exit. (return [<expression>]) is equivalent to (return-from nil [<expression>]), and serves as a single-level exit.

Selection
The if Statements
Single-Branch Selection
The single-branch selection statement is usually of the form if (<expression>) <statement>, where <statement> could be a simple statement, a compound statement, or a block. If the <expression> evaluates to a true value the <statement> is executed. Otherwise it isn't. In either case, the next statement done is the statement following the if statement.

Perl and Ruby have both an if (<expression>) <compound-statement> and an unless (<expression>) <compound-statement>. The latter executes the <compound-statement> if and only if the <expression> is false. A <compound-statement>, complete with its opening and closing bracket indicators, is required even if there is only one statement in it.

Common Lisp's two single-branch selection forms are (when <expression> <form>+) and (unless <expression> <form>+).

The following Ruby program shows several ways to express the single-branch selection statement:

  1. in one line using then and end;
  2. with a newline instead of then, an indented body, and end;
  3. the "modifier" form in which if and the condition follows the modified statement.
Also note the use of the splat in the actual argument.
def sort3(x,y,z)
  # Returns the array [x,y,z] sorted, smallest first.
  if x>y then x,y = y,x end
  if y>z
    y,z = z,y
    x,y = y,x if x>y
  end
  return x,y,z
end

a = [4,3,2]
puts "The result of sorting #{a} is #{sort3(*a)}."
a = [4,2,3]
puts "The result of sorting #{a} is #{sort3(*a)}."
a = [3,4,2]
puts "The result of sorting #{a} is #{sort3(*a)}."
a = [3,2,4]
puts "The result of sorting #{a} is #{sort3(*a)}."
--------------------------------------------------
<timberlake:Test:1:70> ruby sort3.rb
The result of sorting [4, 3, 2] is [2, 3, 4].
The result of sorting [4, 2, 3] is [2, 3, 4].
The result of sorting [3, 4, 2] is [2, 3, 4].
The result of sorting [3, 2, 4] is [2, 3, 4].
In Prolog, since every term either succeeds or fails, every clause is a single-branch selection:
ltCheck(X,Y) :- X=<Y, format("~d and ~d are in order.", [X,Y]).
---------------------------------------------------------------
<timberlake:Test:1:76> prolog
SICStus 4.0.5 (x86_64-linux-glibc2.3): Thu Feb 12 09:48:30 CET 2009
Licensed to SP4cse.buffalo.edu
| ?- ['ltCheck.pro'].
% compiling /projects/shapiro/CSE305/Test/ltCheck.pro...
% compiled /projects/shapiro/CSE305/Test/ltCheck.pro in module user, 0 msec 720 bytes
yes

| ?- ltCheck(3,5).
3 and 5 are in order.
yes

| ?- ltCheck(5,3).
no

| ?- ltCheck(4,4).
4 and 4 are in order.
yes

Double-Branch Selection
The double-branch selection statement is usually of the form
if (<expression>) <then-statement> else <else-statement>
and is often referred to as the if-then-else statement. If the <expression> evaluates to a true value, <then-statement> is executed. Otherwise <else-statement> is. In either case, only one of the two statements is executed, and execution continues with the statement following the if satement. This is the selection referred to in the Böhm/Jacopini theorem. Of course the then statement could always be empty, reducing to a single-branch selection statement.

The if-then-else statement gave rise to a famous case of syntactic ambiguity. When is the else statement executed in a case like

if (test1)
    if (test2)
        statement1
else
    statement2
The two possibilities are: 1) if test1 is true and test2 is false; 2) if test1 is false. In current languges, this is generally solved by a rule that matches the else with the nearest unmatched if. Thus, in the example above, statement2 would be done in the case that test1 is true and test2 is false. If the other case were wanted, brackets or a special keyword need to be used. In Java, brackets would be used to turn the inner if statement into a one-statement compound statement:
if (test1) {
    if (test2)
        statement1
}
else
    statement2
Other languages end every if with a keyword such as end if, making the two cases
if (test1)
    if (test2)
        statement1
    else
        statement2
    end if
end if
and
if (test1)
    if (test2)
        statement1
    end if
else
    statement2
end if

Common Lisp's double-branch selection expression is

(if test
    then-expression
  else-expression)

Multi-Branch Selection
The multi-branch selection that is the most straightforward extension of the double-branch selection chooses one of multiple statements to execute based on the first of multiple tests to evaluate to true. In the C-based languages, it can be expressed as a nested set of (right-associative) if-then-elses:
if (test1) statement1
else if (test2) statement2
else if (test3) statement3
...
else if (testn) statementn
else default-statement
Notice that if we used indentation and brackets to indicate statement nesting, this would be
if (test1) {statement1}
    else {if (test2) {statement2}
             else {if (test3) {statement3}
                    ...
                            else {if (testn) {statementn}
                                    else {default-statement}}...}}
To flatten this, even syntactically, some languages combine the else with the subsequent if giving an elseif (or elif) keyword. In Python, the above would look like
if test1:
    statement1
elif test2:
    statement2
elif test3:
    statement3
...
elif testn:
    statementn
else:
    default-statement
and does not involve nested selection statements, though the tests are still done in order.

(The C-based languages tend not to have a syntactically distinct multi-branch if statement.)

We can express the general idea in EBNF as

<if statement> -> if <condition> then <statement>
                     {elseif <condition> then <statement>}
                     [else <statement>]
                  endif

Common Lisp's multiple-branch selection expression looks like this latter version:

(cond (test1 expression11 expression12 ...)
      (test2 expression21 expression22 ...)
      (test3 expression31 expression32 ...)
      ...
      (t default-expression1 default-expression2 ...))
For example, consider the Lisp function:
(defun stringCompare (str1 str2)
  "Returns 'smaller, 'greater, or 'equal
     depending on whether str1 is less than,greater than, or equal to str2."
  (cond ((string< str1 str2) 'smaller)
	((string> str1 str2) 'greater)
	(t 'equal)))
-------------------------------------------------------------
<timberlake:Test:1:80> composer
International Allegro CL Enterprise Edition
...
cl-user(1): :cl stringCompare
;;; Compiling file stringCompare.cl
;;; Writing fasl file stringCompare.fasl
;;; Fasl write complete
; Fast loading /shared/projects/shapiro/CSE305/Test/stringCompare.fasl

cl-user(2): (stringCompare "aa" "aaa")
smaller

cl-user(3): (stringCompare "abc" "aba")
greater

cl-user(4): (stringCompare "abcd" "abcd")
equal

cl-user(5): :ex
; Exiting
Erlang looks very similar:
-module(stringCompare).
-export([stringCompare/2]).

stringCompare(Str1,Str2) ->
  % Returns 'smaller, 'greater, or 'equal
  %   depending on whether str1 is less than,greater than, or equal to str2.
  if
    Str1<Str2 -> smaller;
    Str1>Str2 -> greater;
    true -> equal
  end.
--------------------------------------------------------
<timberlake:Test:1:83> erl
Erlang R13B03 (erts-5.7.4) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.4  (abort with ^G)

1> c(stringCompare).
{ok,stringCompare}

2> stringCompare:stringCompare("aa", "aaa").
smaller

3> stringCompare:stringCompare("abc", "aba").
greater

4> stringCompare:stringCompare("abcd", "abcd").
equal

5> q().
ok
However, what is allowed as a "guard" in Erlang is limited. For example, user-defined functions are not allowed. (See Erlang/OTP System Documentation, section 5.7.24.)

Prolog also looks somewhat similar:

stringCompare(Str1,Str2,Result) :-
	%% Returns 'smaller, 'greater, or 'equal
	%%   depending on whether str1 is less than,greater than, or equal to str2.
	Str1 @< Str2, Result=smaller;
	Str1 @> Str2, Result=greater;
	Result=equal.
------------------------------------------------------
<timberlake:Test:1:84> prolog
SICStus 4.0.5 (x86_64-linux-glibc2.3): Thu Feb 12 09:48:30 CET 2009
Licensed to SP4cse.buffalo.edu

| ?- ['stringCompare.pro'].
% compiling /projects/shapiro/CSE305/Test/stringCompare.pro...
% compiled /projects/shapiro/CSE305/Test/stringCompare.pro in module user, 0 msec 720 bytes
yes

| ?- stringCompare("aa", "aaa", X).
X = smaller ?
yes

| ?- stringCompare("abc", "aba", X).
X = greater ?
yes

| ?- stringCompare("abcd", "abcd", X).
X = equal ?
yes

| ?- halt.
These are all similar in syntax to Edsger Dijkstra's guarded if:
if <test1> -> <statement1>
[] <test2> -> <statement2>
[] ...
[] <testn> -> <statementn>
fi
However, the semantics are different:
Standard multi-branch if:
Evaluate the tests in order and execute the statement (evaluate the expressions) of the first test that evaluates to true. If none are true, imperative languages tend to do nothing, expression languages typically evaluate to false, or give a run-time error.
Dijkstra's guarded if
Evaluate all the tests, and, nondeterministically, execute the statement of one of the tests that evaluates to true. If none of the tests evaluates to true, it is a run-time error.

Note that nondeterministic execution is not captured by the Böhm/Jacopini control structures, but can be simulated by random selection.

The case Statement
The case statement, introduced by ALGOL W (1966), is a special purpose multiple-branch selection statement for use when all the tests test the same expression for equality to various values.

The general form of the case statement is captured by the EBNF for the Ruby version:

case <test expression>
  {when <key expression> {, <key expression>} then <result expression>}
  [else <default expression>]
end
The <test expression> is evaluated once and compared to each <key expression> in order. The <result expression> of the first <key expression> that passes the comparison is evaluated, and its value is the value of the case expression. If none passes the test and there is an <default expression>, then it is evaluated and its value is returned as the value of the case expression. If no <key expression> passes the test and there is no else, then the value of the case expression is nil.

Ruby compares the <test expression> to each <key expression> using the === operator.

<lhs> === <rhs> is true if
  • <lhs> == <rhs>
  • <rhs> is in the range <lhs>
  • the string <rhs> matches the regular expression <lhs>
  • <rhs> is an instance of the class <lhs>

Here's a Ruby program to read numeric scores and convert them to letter grades according to this course's curve. It uses a while loop, a single-branch if, a double-branch if, and a case expression.

def grade(x)
  # Returns the letter grade corresponding to the numerical percentage x
  case x
  when 0..39 then :F
  when 40..53 then :D
  when 54..59 then :"D+"
  when 60..62 then :"C-"
  when 63..66 then :C
  when 67..69 then :"C+"
  when 70..74 then :"B-"
  when 75..79 then :B
  when 80..84 then :"B+"
  when 85..89 then :"A-"
  when 90..100 then :A
  end
end

while true
  puts "Enter a numeric score between 0 and 100, `done' when finished."
  input = gets.chomp
  exit if input == "done"
  score = input.to_i
  if (0..100) === score
    puts "The score #{score} corresponds to a grade of #{grade(score)}."
  else
    puts "The number #{score} is not a valid numeric score between 0 and 100."
  end
end
-------------------------------------------------------
<timberlake:Test:1:57> ruby gradeProg.rb
Enter a numeric score between 0 and 100, `done' when finished.
92
The score 92 corresponds to a grade of A.
Enter a numeric score between 0 and 100, `done' when finished.
68
The score 68 corresponds to a grade of C+.
546
The number 546 is not a valid numeric score between 0 and 100.
Enter a numeric score between 0 and 100, `done' when finished.
done
<timberlake:Test:1:58>

Common Lisp's case expression is syntactically similar:

(case keyform
  (keylist1 expression11 expression12 ...)
  (keylist2 expression21 expression22 ...)
  (keylist3 expression31 expression32 ...)
  ...
  (t default-expression1 default-expression2 ...))
However, the semantics is a bit different. Each keylist must be a list of literal values (they are not evaluated). To evaluate the case expression, the keyform is evaluated. If its value is listed in one of the keylists the expressions of that keylist are evaluated in order, and the value of the case expression is the value of the last such expression. If the value of the keyform is not listed in one of the keylists, and the optional t case is present, the default-expressions are evaluated, and the value of the last one of those is the value of the case expression. No key may appear in more than one keylist.

Common Lisp also has typecase to test the type of the value of the keyform.

Several languages limit the case keys to be ordinal values. That way, the case statement may be compiled into a table of instructions indexed by the key.

The case statement of the C-based languages is

switch (keyform) {
  case key1: statement11 statement12 ...
  case key2: statement21 statement22 ...
  case key3: statement31 statement32 ...
  ...
  [default: default-statement1 default-statement2 ...]
}
These keys are limited to ordinals, and there can only be one key per case. To handle the situation of allowing the same set of statements to be executed for several different keys, the C-based languages specify that control flows from the last statement of the chosen case directly through to the first statement of the next listed case. If the programmer does not want this to happen, a break statement must be used. For example,
switch (keyform) {
  case 1: 
  case 3:
  case 5:
  case 7:
  case 9: statement-odd;
          break;
  case 2:
  case 4:
  case 6:
  case 8: statement-even;
          break;
  default: statement-too-big;
}
Often, there is only one key in each case, and the break can easily be forgotten, which makes the program act not as it was intended to.

Loop
Iterative Loops
Logically Controlled Loops
Pretest Loops
The pretest logically controlled loop, referred to as the while loop, is the loop considered in the proof of the Böhm/Jacopini theorem. The version used in the C-based languages is a typical example:
while (test) statement
The semantics of this (in HOSL) is:
loop: if not test goto out
      statement
      goto loop
out:  ...
Notice that test is evaluated each time around the loop, and that statement might never be executed.

Posttest Controlled Loops
The C-based languages also have a posttest logically controlled loop, called the do-while:
do statement while (test);
Its semantics is:
loop: statement
      if test goto loop
Notice that statement is always executed at least once, and, again test is evaluated each time around the loop.

A variant that several languages have is called the repeat-until loop and looks like:

repeat statement until test;
Its semantics is:
loop: statement
      if not test goto loop
The repeat-until is very similar to the do-while, but often easier to think about because of the opposite sense of its test.

Loop Forever
The most flexible iterative loop is the one that loops forever, until an exit statement is executed within its body. The Common Lisp version is
(loop {expression})

In many languages, it may be simulated by

while (true) statement
or, in C-based languages by
for (;;;) statement
Recall the earlier example of using the exit statement to process an entire file:
while (true) {
    input := read(file);
    if (input == eof) exit;
    process(input);
}

Counter-Controlled Loops
The oldest counter-controlled loop, Fortran's DO loop illustrates all the issues:
       DO label variable = initial-expression, terminal-expression [, stepsize-expression]
       statements
label  last-statement
       next-statement
The semantics of this are [Sebesta, p. 331-332]
       init-value := initial-expression
       terminal-value := terminal-expression
       step-value := stepsize-expression
       variable := init-value
       iteration-count := max(int((terminal-value - init-value + step-value)
                                  / step-value),
                              0)
loop:  if iteration-count <= 0 goto out
       statements
label: last-statement
       variable = variable + step-value
       iteration-count = iteration-count - 1
       goto loop
out:   next-statement
Note:
  • The loop parameter expressions are evaluated only once, so changing variables that are part of them, doesn't affect the number of times the loop is executed.
  • The number of times the loop is exected is controlled by the iteration-count, not the value of the variable, so assigning to the loop variable inside the loop doesn't affect the number of times the loop is executed.
  • The loop label is available to be the target of goto's inside the loop body. The effect would be to skip the rest of the loop body (except the last-statement---therefore the CONTINUE statement) and continue with the next iteration.
  • The scope of the loop variable is not limited to the loop body, and it retains its last value when the loop is terminated.

The counter-controlled loop statement of the C-based languages is different in several respects from Fortran's DO loop. Their format is:

for (init-expr1, ..., init-exprk;
      terminal-expr1, ..., terminal-exprn;
       step-expr1, ..., step-exprm) 
    statement
The semantics is
{    init-expr1
     ...
     init-exprk
loop:
     terminal-expr1
     ...
     if not terminal-exprn goto out
     statement
bottom:
     step-expr1
     ..., 
     step-exprm
     goto loop
  }
out:
Note:
  • The loop parameter expressions are arbitrary expressions, except that, in Java, the terminal expressions must be Boolean expressions.
  • The loop parameter expressions are evaluated each time through the loop, so changing variables that are part of them, does affect the number of times the loop is executed.
  • The number of times the loop is exected is controlled by the last terminal expression, which is evaluated each time through the loop, so assigning to any variables it uses inside the loop does affect the number of times the loop is executed.
  • If a continue statement is executed inside the loop, control transfers to the bottom label. The effect would be to skip the rest of the loop body and continue with the next iteration. (continue may also be used inside while and do-while loops.)
  • The scope of any variable declared inside the init expressions is limited to the loop parameter expressions and the loop body, and so their last values are not available when the loop is terminated.

Just about every current language has a counter-controlled loop.

Loops Through a Collection
Counter-controlled loops usually use the counter, not for itself, but to index the elements in some collection. Several languages do this more directly.

Python doesn't have a counter-controlled loop. Its equivalent is [Lutz, Python Pocket Reference, p. 30]

for target in sequence:
    suite
[else:
    suite]
The loop variable(s) range(s) over all the members of the given sequence. The effect of the counter-controlled loop is achieved by the range function:
>>> range(5)
[0, 1, 2, 3, 4]

>>> range(2,6)
[2, 3, 4, 5]

>>> for i in range(2,6):
...     print i
...
2
3
4
5
The else suite is executed if the loop terminates normally (other than by executing break.

Python allows sequence to be any object for which an Iterator object is defined.

Several other languages, including Common Lisp, Java, Perl, and Ruby, have loops that range over all the elements of some data structure.

Here's a simple example from Common Lisp:

cl-user(4): (loop
                for d in '(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
                do (print d))

Sunday 
Monday 
Tuesday 
Wednesday 
Thursday 
Friday 
Saturday 
nil
Actually, Common Lisp's loop is extremely flexible. See the CSE202 course notes on iteration and the Common Lisp HyperSpec Sect 6.

Java now also has a for-each loop:

import java.util.*;

public class DataLoop {
    public enum Month {January, February, March, April, May, June,
	    July, August, September, October, November, December}

    public static void main(String[] args) {
	HashSet<DataLoop.Month> thirtyDayMonths = new HashSet<DataLoop.Month>();

	thirtyDayMonths.add(Month.September);
	thirtyDayMonths.add(Month.April);
	thirtyDayMonths.add(Month.June);
	thirtyDayMonths.add(Month.November);

	String output = "Thirty days hath: ";

	for (Month m : thirtyDayMonths) {
	    output += m + " ";
	}

	System.out.println(output);

	int[] eightPrimes = {2,3,5,7,11,13,17,19};
	System.out.println();
	System.out.println("Eight primes:");

	for (int p : eightPrimes) {
	    System.out.println(p);
	}
    } // end of main()
} // DataLoop
---------------------------------------------------------
<wasat:Programs:2:262> /util/java/jdk1.5.0/bin/javac DataLoop.java

<wasat:Programs:2:263>/util/java/jdk1.5.0/bin/java DataLoop
Thirty days hath: September June April November 

Eight primes:
2
3
5
7
11
13
17
19

Following Java, we'll call this kind of loop a for-each loop.

List Comprehensions
A useful operation is to create a list by applying a function to the elements of one or more lists. Consider this Python code for making a list of pairs taken from two other lists using a for-each loop.
>>> x = []
>>> for a in [1,2,3]:
... 	for b in ['a','b','c']:
... 		x.append((a,b))
... 
>>> x
[(1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c'), (3, 'a'), (3, 'b'), (3, 'c')]

This can also be done by mapping a function over a list. For example, in Common Lisp:

cl-user(8): (mapcar #'1+ '(1 2 3))
(2 3 4)

cl-user(9): (mapcar #'(lambda (x) (list x (* x x))) '(1 2 3))
((1 1) (2 4) (3 9))

cl-user(10): (mapcan #'(lambda (x) (list x (* x x))) '(1 2 3))
(1 1 2 4 3 9)

cl-user(11): (mapcan #'(lambda (a)
			(mapcar #'(lambda (b) (list a b))
			'(a b c)))
		    '(1 2 3))
((1 a) (1 b) (1 c) (2 a) (2 b) (2 c) (3 a) (3 b) (3 c))

List comprehensions do this with special syntax. List comprehensions are in Erlang, Haskell, and Python. Here's a use of Haskell's list comprehensions:

Prelude> [ (a,b) | a <- [1,2,3], b <- ['a','b','c'] ]
[(1,'a'),(1,'b'),(1,'c'),(2,'a'),(2,'b'),(2,'c'),(3,'a'),(3,'b'),(3,'c')]

Prelude> [x | x<-[2..10], even x]
[2,4,6,8,10]

Prelude> [(x,x*x*x) | x<-[2..10], even x]
[(2,8),(4,64),(6,216),(8,512),(10,1000)]
Look again at the Haskell version of the sieve of Eratosthenes.

Generators
A generator, also called an "iterator", is a function that, each time it is called, returns another member of some data structure (collection). There are generally three parts to a generator function:
  1. A function that takes the collection as argument, and sets up the generator. It may also return the generator function as its value.
  2. The generator function itself, that returns another element of the collection each time it is called.
  3. A way to tell that the collection has been exhausted. Either the generator returns some special value (such as nil), or throws an exception, or there is a special function for the purpose.
The use of generators is called external iteration, whereas internal iteration is the "normal" iteration, such as the use of the for-each loop. [Erich Gamma, Richard Helm, Ralph Johnson, & John M. Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1995, as quoted in David Flanagan & Yukihiro Matsumoto, The Ruby Programming Language, O'Reilly Media, 2008, p. 137.] Generators in Java are classes that implement the Iterator interface. Their three parts are the three methods
  1. iterator()
  2. next()
  3. hasNext()
Java iterators are used "under the covers" in the for-each loop.

Python's and Ruby's for loops also use iterators "under the covers". The two methods of Python's iterators are [Python Library Reference Sect. 2.3.5]:

  1. __iter__(): Returns an iterator object.
  2. next(): Returns the next item, or throws a StopIteration exception.

Here's a Common Lisp example of writing a generator, and using it to solve the Same Fringe Problem:

(defun fringeGen (tree)
  "Returns a generator function
      that generates the fringe (sequence of leaves) of the given tree."
  #'(lambda ()
      (cond ((atom tree) (pop tree))
	    ((atom (first tree)) (pop tree))
	    (t (loop until (atom (first tree))
		   do (setf tree (append (first tree) (rest tree))))
	       (pop tree)))))

(defun sameFringe (tree1 tree2)
  "Returns t if the two trees, tree1 and tree2, have equal fringes;
   otherwise returns nil."
  (loop with gt1 = (fringeGen tree1)
      with gt2 = (fringeGen tree2)
      for l1 = (funcall gt1)
      for l2 = (funcall gt2)
      when (and (null l1) (null l2))
      return t
      unless (eql l1 l2)
      return nil))
---------------------------------------------------------
cl-user(52): (setf nextLeaf (fringeGen '(((a) b) (c d) e)))
#<Closure (:internal fringeGen 0) @ #x71cb11aa>

cl-user(53): (loop for x = (funcall nextLeaf)
		 while x
		 do (print x))
a 
b 
c 
d 
e 
nil

cl-user(54): (sameFringe '(a b c) '(a b c))
t

cl-user(55): (sameFringe '(a b c) '(a c))
nil

cl-user(56): (sameFringe '(a (b c)) '((a b) c))
t

cl-user(57): (sameFringe '(a (b c)) '((a b)))
nil

Recursive Loops
Loops are one of the three classes of control structures of the Böhm/Jacopini theorem. The loops we have been considering have been iterative loops. Recursive loops are an alternative. For example, an iterative Lisp function to call the function visit on every member of a list is
(defun visitAll (list)
  (loop for x in list
      do (visit x)))
whereas a recursive version to do the same thing is
(defun visitAll (list)
  (unless (endp list)
    (visit (first list))
    (visitAll (rest list))))
Every iterative loop may be rewritten as a recursive loop, but some recursive loops may be rewritten as iterative loops only with the aid of an explicit stack.

Common Lisp example of a recursive function that could only be written iteratively with an explicit stack:

(defun polishPrefixEval (stack)

  (when stack

    (let ((op (pop stack)))

      (cond ((numberp op)
	     (cons op (polishPrefixEval stack)))

	    ((member op '(+ - * /))
	     (setf stack (polishPrefixEval stack))
	     (cons (funcall op (first stack) (second stack))
		   (nthcdr 2 stack)))

	    (t (error "*** Bad stack element ~s ***" op))))))
--------------------------------------------------------------------------------
cl-user(118): (trace polishPrefixEval)

cl-user(119): (polishPrefixEval '(+ * 2 3 4))
 0[2]: (polishPrefixEval (+ * 2 3 4))
   1[2]: (polishPrefixEval (* 2 3 4))
     2[2]: (polishPrefixEval (2 3 4))
       3[2]: (polishPrefixEval (3 4))
         4[2]: (polishPrefixEval (4))
           5[2]: (polishPrefixEval nil)
           5[2]: returned nil
         4[2]: returned (4)
       3[2]: returned (3 4)
     2[2]: returned (2 3 4)
   1[2]: returned (6 4)
 0[2]: returned (10)
(10)

cl-user(120): (polishPrefixEval '(* + 2 3 4))
 0[2]: (polishPrefixEval (* + 2 3 4))
   1[2]: (polishPrefixEval (+ 2 3 4))
     2[2]: (polishPrefixEval (2 3 4))
       3[2]: (polishPrefixEval (3 4))
         4[2]: (polishPrefixEval (4))
           5[2]: (polishPrefixEval nil)
           5[2]: returned nil
         4[2]: returned (4)
       3[2]: returned (3 4)
     2[2]: returned (2 3 4)
   1[2]: returned (5 4)
 0[2]: returned (20)
(20)

cl-user(121): (polishPrefixEval '(* 2 + 3 4))
 0[2]: (polishPrefixEval (* 2 + 3 4))
   1[2]: (polishPrefixEval (2 + 3 4))
     2[2]: (polishPrefixEval (+ 3 4))
       3[2]: (polishPrefixEval (3 4))
         4[2]: (polishPrefixEval (4))
           5[2]: (polishPrefixEval nil)
           5[2]: returned nil
         4[2]: returned (4)
       3[2]: returned (3 4)
     2[2]: returned (7)
   1[2]: returned (2 7)
 0[2]: returned (14)
(14)

cl-user(122): (polishPrefixEval '(** 2 + 3 4))
 0[2]: (polishPrefixEval (** 2 + 3 4))
Error: *** Bad stack element ** ***

Restart actions (select using :continue):
 0: Return to Top Level (an "abort" restart).
 1: Abort entirely from this (lisp) process.
[1] cl-user(123): :res
 0[2]: returned-by-throwing to tag top-level-reset: nil

Tail Recursion: The value of the recursive call is returned as is. Can be turned into an iterative loop by a compiler. Compare:

(defun sumAllNotTR (list)
  (if list
      (+ (first list) (sumAllNotTR (rest list)))
    0))

(defun sumAllTR (list &optional (value 0))
  (if list
      (sumAllTR (rest list) (+ (first list) value))
    value))
--------------------------------------------------------
cl-user(137): (trace sumAllNotTR sumAllTR)
(sumAllTR sumAllNotTR)

cl-user(138): (sumAllNotTR '(1 2 3 4))
 0[2]: (sumAllNotTR (1 2 3 4))
   1[2]: (sumAllNotTR (2 3 4))
     2[2]: (sumAllNotTR (3 4))
       3[2]: (sumAllNotTR (4))
         4[2]: (sumAllNotTR nil)
         4[2]: returned 0
       3[2]: returned 4
     2[2]: returned 7
   1[2]: returned 9
 0[2]: returned 10
10

cl-user(139): (sumAllTR '(1 2 3 4))
 0[2]: (sumAllTR (1 2 3 4))
   1[2]: (sumAllTR (2 3 4) 1)
     2[2]: (sumAllTR (3 4) 3)
       3[2]: (sumAllTR (4) 6)
         4[2]: (sumAllTR nil 10)
         4[2]: returned 10
       3[2]: returned 10
     2[2]: returned 10
   1[2]: returned 10
 0[2]: returned 10
10

Backtrack Control Structures
Based on success/fail tests.
test1, test2, ..., testi, testi+1, ... testn
If testi succeeds, try testi+1
If testi fails, back up to testi-1, and try to succeed another way
If testn succeeds, done
If test1 fails ultimately, entire set fails.

Consider matching the pattern [abc]*abcbe against the string abcaabcbabccdabcbe
See XEmacs 21.5 HTML Manuals Sect. 12.4

Prolog uses backtracking as its main control structure:

birthday(arthur, "Dec 3, 1980").
birthday(bea, "March 15, 1985").
birthday(chuck, "Dec 3, 1980").
birthday(dave, "March 15, 1985").
birthday(ethel, "June 17, 1975").
birthday(fran, "March 15, 1985").

findSame :- birthday(X,D),
	format("1: ~a's birthday is ~s.~n", [X,D]),
	birthday(Y,D),
	format("2:    ~a's birthday is ~s.~n", [Y,D]),
	Y \== X,
	format("      ~a and ~a have the same birthday.~n", [X,Y]),
	birthday(Z,D),
	format("3:       ~a's birthday is ~s.~n", [Z,D]),
	Z \== Y, Z \== X,
	format("         ~a, ~a, and ~a have the same birthday.~n", [X,Y,Z]).
-----------------------------------------------------------------------------
<wasat:Programs:2:273> sicstus -l birthdays.prolog --goal "findSame,halt."
...

1: arthur's birthday is Dec 3, 1980.
2:    arthur's birthday is Dec 3, 1980.
2:    chuck's birthday is Dec 3, 1980.
      arthur and chuck have the same birthday.
3:       arthur's birthday is Dec 3, 1980.
3:       chuck's birthday is Dec 3, 1980.
1: bea's birthday is March 15, 1985.
2:    bea's birthday is March 15, 1985.
2:    dave's birthday is March 15, 1985.
      bea and dave have the same birthday.
3:       bea's birthday is March 15, 1985.
3:       dave's birthday is March 15, 1985.
3:       fran's birthday is March 15, 1985.
         bea, dave, and fran have the same birthday.

Dijkstra's guarded loop
do <test1> -> <statement1>
[] <test2> -> <statement2>
[] ...
[] <testn> -> <statementn>
od
Evaluate the tests, and, if any evaluate to true, nondeterministically execute the statement of one of the tests that evaluates to true, and then, do it all again. When none of the tests evaluates to true, the loop terminates.

First Previous Next

Copyright © 2003-2010 by Stuart C. Shapiro. All rights reserved.

Last modified: Mon Mar 22 10:46:12 2010
Stuart C. Shapiro <shapiro@cse.buffalo.edu>