Synopsis
Roughly, sequence control structures fall into one of the three following groups
How is the following expression evaluated :-
a*3 * sqrt(0.8) / 3 * 8.
It is common for a language implementation to specify the tree representation of this structure.
The basic sequence control mechanism is function composition. Function composition defines the structure of the tree representation.
Note that a tree structure leaves part of the order of evaluation undefined. For example, in the expression above, is sqrt(0.8) done before a*3. This is often left unspecified (so that it may be optimised).
Postfix notation: The above expression could be rewritten as
a 3 * 0.8 sqrt * 3 / 8 *Evaluating this expression is a simple matter of adding each operand to a stack, then when each operator is found, it is applied to the appropriate number of operands on the top of the stack, then the result is pushed back on the stack.
Prefix notation: As above but the operator comes first
* / * * a 3 sqrt 0.8 3 8Cambridge notation uses brackets to surround each operator with its operands
(* (/ (* (* a 3) (sqrt 0.8)) 3) 8)
Infix notation:
a*3 * sqrt(0.8) / 3 * 8.
Evaluation
Infix notation is inherently ambiguous.. there must be a set of rules that
describe which operators have precedence over which others.. it is not suited
for writting operators with other than two operands..
i.e. biggest (1, 2, 0),
it is more difficult to evaluate. Most compilers transform infix notation
into either pre- or post- fix notation prior to producing machine code.
Hierarchy of Operators
Most languages specify precedence with a precedence table.
In general associativity, is left-to right, i.e. a*b*c is ((a*b)*c).
As you are aware, some languages are written using prefix (like LISP) and thus do not need precedence tables. Some (like Smalltalk) assume almost everything is the same precedence.
Some other problems with infix notation include.
Some languages offer quite powerful pattern matching facilities,i.e. $_ =~ s/$pattern{$position}/$replaceString/g;, where the language is doing many operations to check the validity of the statement.
Term rewriting is a restricted form of pattern matching.. in some languages
it can be used to emulate procedures.. In ML
fun fact(1) = 1
| fact(N::int) = N * fact (N-1)
When ML processes a function call, it will replace it with the appropriate
definition.
Unification (PROLOG) is another form of nonarithmetic expression
Substitution is the process of replacing one thing with another, i.e. (C)
#define macro1(N) printf("Hello N");
whenever the program sees something of the form, macro1(jill),
it will replace it with print("Hello jill");
Basic Statements
Most languages assume assignment is a separate statement giving expressions of the form A:= B+7, but in C, assignment is an operator that returns the value assigned, so if ((A=7)==C) and A=B=C=D=7 are legal.
Input/Output is another area where assignment to variables occur. It is worth remembering that most input and output is buffered.. the operation often does not finish until a new line is read or printed.
Finally, assignment occurs in many other locations, such as parameter passing and unification.
Forms of Statement-Level Control
Most languages provide unconditional "GOTO label", and conditional "if A=0 then GOTO label", GOTO statements. They equate to a machine code instruction that transfers control to another memory location. GOTOs are claimed to be good because they are
Many languages provide a special command (Ada - exit, C - break) that will jump from the inside of a loop to just past its end. C also provides the continue command that jumps to the end of the loop (but still inside it). Both these structures are still considered structured as they still have only one exit.
Introduced by ALGOL 60 in the form of begin...end
A block is a compound statement that can define a new scope (with local variables)
Most structured languages have some sort of compound statement that collects terms together.. C {}, Algol 68 begin .. end, Smalltalk [].
Conditional Statements
The common if statements.. If test then statements endif and if test then statements else statements2 endif are implemented by simple machine code statements that check a value, then dependent on whether it is true or false, jump to the appropriate location.
Nested Selectors
Pascal:
if ... then
if ... then
...
else ...
Which "then" gets the "else"? Pascal's rule: else goes with the nearest then
Alternate solution: Closing Reserved Words
In Ada:
CASE statements are more complicated as often multiple tests
must be performed.
Case design issues:
Iteration Statements
The repeated execution of a statement or compound statement is accomplished either by iteration or recursion
Here we look at iteration, because recursion is unit-level control
General design issues for iteration control statements:
Two common strategies: counter-controlled, and logically-controlled
Design Issues:
FORTRAN 77 and 90
Syntax: DO label var = start, finish [, stepsize] Stepsize can be any value but zero Parameters can be expressions Design choices: Loop var can be INTEGER, REAL, or DOUBLE Loop var always has its last value Loop parameters are evaluated only once The loop var cannot be changed in the loop, but the parameters can; because they are evaluated only once, it does not affect loop control
ALGOL 60 For Loop
Syntax: for var :=do statement where can have: list of expressions expression step expression until expression expression while boolean_expression for index := 1 step 2 until 50, 60, 70, 80, index + 1 until 100 do (index = 1, 3, 5, 7, ..., 49, 60, 70, 80, 81, 82, ..., 100) ALGOL 60 For Design Choices Control expression can be int or real; its scope is whatever it is declared to be Control var has its last assigned value after loop termination The loop var cannot be changed in the loop, but the parameters can, and when they are, it affects loop control Parameters are evaluated with every iteration, making it very complex and difficult to read
Pascal For Loop
Syntax: for var := initial (to | downto) final do statement Design Choices: Loop var must be an ordinal type of usual scope After normal termination, loop var is undefined The loop var cannot be changed in the loop The loop parameters can be changed, but they are evaluated just once, so it does not affect loop control
Ada For Loop
Syntax: for var in [reverse] discrete_range loop ... end loop; Design choices: Type of the loop var is that of the discrete range; its scope is the loop body (it is implicitly declared) The loop var does not exist outside the loop The loop var cannot be changed in the loop, but the discrete range can; it does not affect loop control The discrete range is evaluated just once
Theory of Prime Programs
Consider 3 classes of flowchart nodes:
Any flowchart is a graph of directed arcs and these 3 types of nodes:
A proper program is a flowchart with:
1 entry arc
1 exit arc
There is a path from entry arc to any node to exit arc
A prime program is a proper program which has no embedded proper
subprogram of greater than 1 node.
i.e., cannot cut 2 arcs to extract a prime subprogram within it.
A composite program is a proper program that is not prime.
Prime Program Decomposition
Every proper program can be decomposed into a hierarchical set of prime subprograms:
This decomposition is unique (except for special case of linear sequences
of function nodes).
Enumeration of Primes
All prime programs can be enumerated:
Each implements a function.
What is function for 2-node primes (c) and (d) and 4-node primes (l)
through (q)?
Structure Theorem of Bohm-Jacobini
Use of prime programs to define structured programming:
Concept first used by Dijkstra in 1968 as gotoless programming.
Called structured programming in early 1970s-
Program only with if, while and sequence control structures.
Issue in 1970s: Does this limit what programs can be written?
Resolved by Structure Theorem of Bohm-Jacobini.
An Example
**** original program with goto's
x := 0 ;
A: if (p(x)) goto B ;
x := f(x) ;
if (q(x)) goto C ;
B: X := g(x) ;
goto A :
C: print X ;
exit ;
**** original program with line numbers
1 x := 0 ;
2 A: if (p(x)) goto B ;
3 x := f(x) ;
4 if (q(x)) goto C ;
5 B: x := g(x) ;
6 goto A :
7 C: print x ;
8 exit ;
**** Using variable LN to simulate line numbers (use LN == 0 for exit)
LN := 1 ;
while (LN <> 0) {
if (LN == 1) {
x := 0 ;
LN := 2 ;
} else if (LN == 2) {
if (p(x))
LN := 5 ;
else
LN := 3 ;
} else if (LN == 3) {
x := f(x) ;
LN := 4 ;
} else if (LN == 4) {
if (q(x))
LN := 7 ;
else
LN := 5 ;
} else if (LN == 5) {
x := g(x) ;
LN := 6 ;
} else if (LN == 6) {
LN := 2 ;
} else if (LN == 7) {
print x ;
LN := 8 ;
} else if (LN == 8) {
LN := 0 ;
}
}
**** A little more efficient
x := 0 ;
LN := 1 ;
while (LN <> 0) {
if (LN == 2) {
if (p(x))
LN := 5 ;
else {
x := f(x) ;
if (q(x))
LN := 7 ;
else
LN := 5 ;
}
} else if (LN == 5) {
x := g(x) ;
LN := 2 ;
} else if (LN == 7) {
print x ;
LN := 0 ;
}
}