Chapter 8: Statement-Level Control Structures
Chapter 8: Statement-Level Control Structures
Topics Overview
Introduction
Selection Statements
Iterative Statements
Unconditional Branching
Guarded Commands
Conclusions
Levels of Control Flow
Control flow can be categorized into three levels:
Within expressions (discussed in Chapter 7)
Among program units (discussed in Chapter 9)
Among program statements (focus of this chapter)
Control Statements: Evolution
Historical Context:
The control statements in FORTRAN I were closely tied to the hardware of IBM 704.
The 1960s saw significant research and debate regarding control structures leading to a significant conclusion:
All algorithms depicted by flowcharts can be coded using only two-way selection and pretest logical loops.
Control Structure Definition
A control structure is defined as a control statement along with the statements it controls for execution.
Design Question: Should a control structure allow for multiple entries?
Selection Statements
Purpose: A selection statement facilitates the choice between multiple paths of execution.
Categories:
Two-way selectors
Multiple-way selectors
Two-Way Selection Statements
General Form:
if control_expression then clause else clause
Design Issues:
Form and type of the control expression?
Specification method of the then and else clauses?
Clarity in meaning for nested selectors?
The Control Expression
If neither the
thenkeyword nor any syntactic marker introduces thethenclause, the control expression must be enclosed in parentheses.In languages like C89, C99, Python, and C++, the control expression can be an arithmetic expression; most others require it to be Boolean.
Clause Form
In several modern languages, the then and else clauses may either be single statements or compound statements.
Perl: All clauses are required to be defined by braces (denoting they must be compound).
Python and Ruby: Clauses are statements sequences, with Python using indentation for clause definition.
if x > y:
x = y
print "x was greater than y"
Nesting Selectors Examples
Java Example:
if (sum == 0)
if (count == 0)
result = 0;
else
result = 1;
In Java, the
elseassociates with the nearest precedingifdue to static semantics rules.To enforce different semantics, one can use compound statements:
if (sum == 0) {
if (count == 0)
result = 0;
} else
result = 1;
}
Python Example:
if sum == 0:
if count == 0:
result = 0
else:
result = 1
Selector Expressions
In languages such as ML, F#, and Lisp, a selector can be formulated as an expression.
Example in F#:
let y =
if x > 0 then x
else 2 * x
An
elseclause is essential if theifexpression is expected to yield a value, ensuring that the types of values returned by thethenandelseclauses are consistent.
Multiple-Way Selection Statements
These statements enable the selection of one from many statements or statement groups, with several design issues:
Control expression type and form?
Definition of selectable segments?
Execution flow limitations: is it restricted to a single selectable segment?
Specification of case values?
Handling of unrepresented expression values?
Examples of Multiple-Way Selection
C, C++, Java, and JavaScript: The
switchstatement is used.
switch (expression) {
case const_expr1: stmt1;
...
case const_exprn: stmtn;
[default: stmtn+1]
}
C Language Design Choices:
The control expression is limited to integer types.
Selectable segments can be statements or compound statements.
There's the possibility of executing multiple segments in a single execution without an implicit branching at segment end.
The default clause addresses any unrepresented cases (without a default, no execution occurs).
C#:
The
switchdiffers from C as it prohibits implicit multisegment execution.Each segment must close with an unconditional branch (either
gotoorbreak).The control expression can also use strings as case constants.
Ruby Case Statement Example
Ruby includes two forms for case statements, one defined as:
leap = case
when year % 400 == 0 then true
when year % 100 == 0 then false
else year % 4 == 0
end
Implementing Multiple Selectors
Strategies for implementation may include:
Utilizing multiple conditional branches.
Storing case values within a table and employing linear table searching.
Using a hash table when there are more than ten cases.
If most of the overall range of case values are represented (over half), one can implement an array indexed by case values with associated case labels.
Multiple-Way Selection Using if Statements
Extensions to two-way selectors utilizing
else-ifclauses.Example in Python:
if count < 10:
bag1 = True
elif count < 100:
bag2 = True
elif count < 1000:
bag3 = True
The above Python logic is convertible to a Ruby case:
case
when count < 10 then bag1 = true
when count < 100 then bag2 = true
when count < 1000 then bag3 = true
end
Scheme’s Multiple Selector Example
General structure of a call to
COND:
(COND
(predicate1 expression1)
...
(predicaten expressionn)
[(ELSE expressionn+1)]
)
The
ELSEclause is optional and synonymous withtrue. Each predicate-expression pair acts as a parameter.Semantics: The value of the evaluation of
CONDcorresponds to the expression linked with the first true predicate expression.
Iterative Statements
Iteration allows repeated execution of statements or compound statements, achieved via either iteration or recursion.
General design issues for iteration control statements root from:
How iteration is governed?
The position of the control mechanism within the loop.
Counter-Controlled Loops
A counting iterative statement features a loop variable along with specifications for the initial value, terminal value, and stepsize.
Design Issues:
Type and scope considerations for the loop variable?
Is modification of the loop variable or parameters within the loop body allowed?
Should loop parameters only evaluate once or with each iteration?
The value of the loop variable post-termination?
Examples of Counter-Controlled Loops
C-Based Languages Syntax:
for ([expr_1]; [expr_2]; [expr_3]) statement
Here: expressions can encompass whole statements or sequences segmented by commas, with the last statement's value acting as the expression’s evaluation.
If the second expression is absent, it defaults to an infinite loop.
Design Choices in C include: no explicit loop variable, all variables are mutable in the loop, the first expression evaluates once, while the second condition repeats across iterations, and branching into the body of the loop is sanctioned.
C++ Differences from C:
Control expressions can also be Boolean.
Initial expressions may define variables (scope from definition through loop body end).
Java and C#:
Control expressions must strictly be Boolean.
Python For Loop Syntax:
for loop_variable in object:
loop body
[else:
- else clause]
Typical object: a range defined either as a list or calling the range function.
The loop variable adopts the values from the specified range per iteration, and at termination, it retains the last assigned value. The optional else clause executes if the loop concludes without a break.
F# Counter-Controlled Loops:
Lacking variables, counter-controlled loops are simulated through recursive functions:
let rec forLoop loopBody reps =
if reps <= 0 then ()
else
loopBody();
forLoop loopBody (reps - 1)
The above syntax defines a recursive function with parameters for the loop body and repetition count, where
()signifies no output or return value.
Logically-Controlled Loops
For these loops, repetition control hinges on a Boolean expression.
Design Issues:
Pretest versus posttest nature?
Should logically controlled loops be a specific case of counting loops or stand as a separate statement?
Examples of Logically-Controlled Loops
C/C++ Syntax: Two forms exist for pretest and posttest iterations, where control expressions can be arithmetic:
while (control_expr) do loop body
loop body while (control_expr)
Valid to branch into a logically controlled loop body; Java mimics this but mandates Boolean types for control expressions, entering the body exclusively at the start.
F# Simulated Logically-Controlled Loops:
let rec whileLoop test body =
if test() then
body();
whileLoop test body
else ()
The function
whileLoopis recursively defined with parameters for the control check and loop body, where test defines the control condition.
User-Located Loop Control Mechanisms
Programmers may prefer defining loop control locations distinct from the loop's top or bottom.
Design considerations for nested loops include:
Should the exit condition form part of the exit?
Is it permissible to transfer control out of multiple loops?
Examples of User-Located Loop Control
Languages enabling unconditional unlabeled exits (e.g.,
break): C, C++, Python, Ruby.Languages with unconditional labeled exits: Java and Perl (using
breakin Java, andlastin Perl).Language paradigms like C, C++, and Python providing an unlabeled control statement (e.g.,
continue) can skip current iteration while remaining within the loop. Java and Perl offer labeled alternatives.
Iteration Based on Data Structures
Loop iteration is controlled by the element count within a data structure.
The control mechanism may trigger an iterator function that returns the next data structure element until completion, leading to loop termination.
Examples of Iteration Based on Data Structures
C's For Loop as User-defined Iterator:
for (p=root; p!=NULL; traverse(p)){
...
}
PHP Iteration Rights:
currentpositions at an array element.nextadvances current to the next element.resetmoves current back to the first element.
Java 5.0 Iterator Syntax:
Iteration utilizes the for statement, often referred to as foreach. For classes implementing the Iterable interface, such constructs can be employed, e.g.,
ArrayList.
C#, F# and .NET Variants:
Utilize generic library classes, allowing iteration through arrays, lists, stacks, and queues with foreach. User-defined collections may also implement the IEnumerator interface for foreach use.
List<String> names = new List<String>();
names.Add("Bob");
names.Add("Carol");
names.Add("Ted");
foreach (String name in names)
Ruby Iteration With Blocks
Ruby blocks consist of code sequences framed in either braces or
do...end.These blocks can function collaboratively with methods to create iterators.
Predefined Iteration Methods in Ruby:
3.times {puts "Hey!"}
list.each {|value| puts value}
1.upto(5) {|x| print x, " "}
Blocks function via attaching method calls and can retain parameters (through vertical bars), executing yield statements when engaged.
Example of Ruby Iterator Utilizing Yield Statement:
first, second = 1, 1
def fibonacci(last)
while first <= last
yield first
first, second = second, first + second
end
end
puts "Fibonacci numbers less than 100 are:"
fibonacci(100) {|num| print num, " "}
Ruby has a for statement that translates into upto method calls internally.
Python's Yield Statement
Illustrated with a function that generates nodes within a data structure:
def traverse(self):
yield node
Every call to
traversereturns the next node in the structure.The
yieldstatement functions similarly toreturn, but alters the function's semantics significantly as it operates in an independent control thread, resuming from its last execution state in subsequent calls, thereby marking it as history-sensitive.
Unconditional Branching
Definition: Unconditional branching entails the transfer of execution control to a specific location within the program code.
Historical Context: The use of this feature was a topic of intense debate through the 1960s and 1970s, primarily concerning readability concerns.
Languages like Java do not support the
gotostatement, whereas C# permits its use (notably within switch statements).Other loop exit statements can be regarded as camouflaged forms of
goto.
Guarded Commands
Creation: Designed by Edsger Dijkstra to facilitate a new programming methodology aimed at supporting verification (correctness) in program development.
Serve as a foundation for concurrent programming mechanisms introduced in CSP (Communicating Sequential Processes).
Fundamental Principle: The order of evaluation shouldn't hold importance if specified by the program.
Selection Guarded Command
Structure:
if <Boolean expr> -> <statement> [] <Boolean expr> -> <statement> ... [] <Boolean expr> -> <statement> fi
Semantics: Upon reaching this construct:
All Boolean expressions are evaluated.
If multiple conditions hold true, one is chosen non-deterministically.
If none are true, it results in a runtime error.
Loop Guarded Command
Structure:
do <Boolean> -> <statement> [] <Boolean> -> <statement> ... [] <Boolean> -> <statement> od
Semantics: During each iteration:
Evaluate all Boolean expressions.
If more than one condition is true, select one non-deterministically and repeat the loop.
If no conditions are valid, the loop exits.
Rationale Behind Guarded Commands
The interplay between control statements and program verification is crucial.
Verification is unfeasible with
gotostatements.It's achievable via mechanisms incorporating only selection and logical pretest loops.
Verification simplifies considerably when structured solely around guarded commands.
Conclusions
A diverse range of statement-level structures exists.
Selecting appropriate control statements, beyond simple selections and logical pretest loops, involves balancing language size with usability (writability).
Notably, functional and logic programming languages deploy control structures that diverge markedly from procedural habituations.