COSC3306: Programming Paradigms Lecture 4: Imperative Programming Specifications

Download Report

Transcript COSC3306: Programming Paradigms Lecture 4: Imperative Programming Specifications

COSC3306:
Programming Paradigms
Lecture 4: Imperative
Programming Specifications
Haibin Zhu, Ph.D.
Computer Science
Nipissing University
(C) 2003
1
Contents
Taxonomy
Design Principles
Control Flows
Execution steps
Undesirable Characteristics
Desirable Characteristics
2
Taxonomy
Control-driven or control-flow
architectures:
– Reduced Instruction Set Computers (RISC).
– Complex Instruction Set Computers (CISC).
– High-level Language Architectures (HLL).
Data-driven or data-flow architectures.
Demand-driven or reduction
architectures.
3
Taxonomy
In control-driven or control-flow architectures,
– The flow of computation is determined by the instruction
sequence, and data are gathered as an instruction
needs them.
In a data-driven or data-flow architectures,
– It is controlled by the readiness or the availability of
data.
In demand-driven or reduction architectures,
– An instruction is enabled for execution when its results
are required as operands for another instruction that
has already been enabled for execution.
4
Properties of an imperative
language
In general, a programming language that is
characterized by the following three properties is
called an imperative language, since its primary
feature is a sequence of statements that
represent commands.
– The sequential execution of instructions,
– The use of variables representing memory location
values, and
– The use of assignment statements to change the
values of variables and to allow the program to
operate on these memory location values.
5
Design Principles
Imperative programming languages
describe how a given sequence of
operations computes the result of a given
problem.
In the imperative approach we are more
concerned with how to formulate the
solution in terms of our primitive
operations.
6
Three categories of action
Computational actions such as arithmetic
operations.
Control-flow actions such as comparisons
and looping statements.
Input-output actions such as read and
write operations.
7
Memory location
Imperative actions all have access to a common
storage which consists of an arbitrary number of
memory locations. Each memory location can be
characterized by a state as the following:
– It may contain a data, meaning that the location is
allocated and containing data.
– It may be undefined, meaning that the location is
allocated but not yet containing data.
– It may be unused, meaning that the location is not
allocated.
8
Stable data
Data stored in memory locations are
stable. In other words, data contained in a
memory location remains undisturbed and
available for inspection until that memory
location is deallocated or has a new data
stored on it. An imperative action works by
making changes in memory.
9
Possible changes
Store data in a location. This action stores data
in a memory location.
Deallocate a memory location. This action
changes the state of memory location to unused.
Allocate a memory location. This action finds an
unused memory location and changes its status
to allocated.
Retrieve a memory location. This action yields
the data currently contained in a memory
location.
10
Variables
A major component of a computer system
is the memory which is comprised of a
large number of memory cells (locations).
The memory is where the data can be
stored.
The memory cells must be named.
11
Functions of variable declaration
In variable declaration we consider the memory
locations in which the values of interest reside. The
variable declaration performs three functions as the
following:
– It allocates an area of memory of a specified size.
– It attaches a symbolic name to the allocated area of
memory. This is called binding a name to memory
locations.
– It initializes the contents of the memory locations if there
is any initialization.
These are three important functions of variable
declarations in most imperative programming
languages.
12
Data Types
The data type used in programming languages can
be classified into either of two categories.
– Scalar data type which is the simplest possible data types.
– Structured data type, in which consist of some combination
of scalar data types.
Any data type has three associated components.
– Type name which is the identifier used to designate the
type in declaration.
– A set of constants of that type which is a symbol for a value
that may be assigned to a variable of compatible type.
– A set of operations upon that type which are used to
construct expressions for the type.
13
Type Equivalence
An important question in the application of
type to type checking is type equivalence,
meaning that when are two types the
same?
This question can be answered in three
different ways as the following.
14
Structural Equivalence
Two data types are the same if they have the
same structure, meaning that they are built in
exactly the same way using the same type
constructors from the same simple types. In
other words, two sets are the same if they
contain the same values. For example, the
types t1 and t2 defined as following are
structurally equivalent, if we were only
concerned about the size of the index set.
–
–
t1  array [1 . . 11] of integer;
t2  array [0 . . 10] of integer;
15
Name Equivalence
Two named types are equivalent if they have
the same name. This is a stronger condition
than structural equivalence, because a name
can refer only to a single type declaration.
Name equivalence is adopted in Ada language.
For example, the types array1 and array2
defined as follows are name equivalence; as
are age and integer.
–
–
–
type array1 is array (1 . . 10) of INTEGER;
type array2 is new array1;
type age is new INTEGER;
In Ada, the keyword new enforces the name
equivalence.
16
Declaration Equivalence
Two type names that goes back to the
same original structure declaration by a
series of redeclarations are considered to
be equivalent types. Modula-2 supports
declaration equivalence, where in C
language a mixture of structural and
declaration equivalence is used.
17
An example
For example, in the following declarations
name1, name2, and name3 are all declaration
equivalent, but not name equivalent.
–
–
–
–
type
name1  array[1 . . 10] of integer;
name2  name1;
name3  name2;
Similarly, in the following Modula-2 declaration
x and y are declaration equivalent variables.
–
VAR x, y : ARRAY[1 . . 10] of INTEGER;
18
Assignment Statements
The notion that every value computed must be assigned
to a memory location is performed by an assignment
statement. For example, the following assignment
statement is a typical action in which it changes the value
of a variable (memory location value) known as class.
class  10;
The assignment symbol “” appears between the right
and left sides. This assignment statement sets class to
10 and the old value of class is discarded.
A similar example is the following assignment statement
class  45;
where the value 9 of the expression 45 is assigned as
the value of class variable by ignoring the old value of
variable.
19
Operand Evaluation
An important design characteristic is the order of
evaluation of operands in expressions.
For example, in the following expression
A  10;
B  A  10;
the value of B is computed by taking the content
of a memory location associated with variable A,
thus B takes the value as 20. If an operand is a
parenthesized expression, then all operators it
contains must be evaluated before its value to
be used as an operand.
20
Another example
For example, in the evaluation of the following
expression
A  10;
B  20;
C  (A  B)  10;
the value of C is computed by evaluating the
value of the subexpression (AB), in which two
operands A and B and one operator “” are
involved. Thus C takes the value 3010 as the
computed value.
21
Side Effects
The important issue in imperative programming
language design is the side effects from the
evaluation of the expressions. If neither of the
operands of an operator has side effects then
operand evaluation order is irrelevant.
For example, there is no side effects associated
with the evaluation of the following
subexpressions of the expression.
A  10;
B  20;
C  (A  5)  (B  10);
22
An example
In contrast, sometimes the evaluation of an
operand has side effects.
For example, the following C expression
illustrates the situation where the stored value of
A in memory to be increased by 10 first, and the
second subexpression then taking this new
stored value as its value.
In this example, the side effect of the evaluation
of the first subexpression causes the value of B
to be evaluated to 50.
A  10;
B  (A  A  10) + (A  10);
23
To be continued
Control constructs
Functions and Procedures
Control Flows
Execution Steps
Undesirable Characteristics
Desirable Characteristics
24
Selection Constructs
An array data structure supports random access to a
sequence of elements of the same type.
Random access means that elements can be selected
by their position in the data structure. An assignment
x  A[i];
assigns to x the value of the ith element of array A. The
assignment
A[i]  x;
changes the value of ith element of array A to x.
Occasionally an algorithm will contain a series of
decisions in which a variable or expression is tested
separately for each of the constant integral values it may
assume, and different actions must be takes.
25
Examples
One way of making such an effect is by
using a list of if statements such as:
if
(condition1)
action1;
if
(condition2)
action2;
.
.
.
if
(conditionN) actionN;
26
Examples
For example, consider the following code
if
AB
Sum  Sum  A;
Acount  Acount  1;
if
BA
Sum  Sum  B;
Bcount  Bcount  1;
27
Alternatives
An alternative is the switch multi-selection structure to handle such decision
making.
switch (expression)
{
case 1:
action1;
break;
case 2:
action2;
break;
.
.
.
case N:
actionN;
break;
default:
actionX;
break;
}
28
Basic issues
Selection structures vary from language to
language, but they tend to agree on the
following issues.
– Case selections can appear in any order.
– Case selections are not required to be
consecutive, meaning that it is valid to have
case-1 and case-4 without case-2 and case-3.
– Case selections must be distinct with regard
to actions of each case; otherwise we need to
combine the actions to avoid any conflict.
29
Conditional Constructs
A conditional statement as the following form
if
expression
statement2
then
statement1
else
control flows through statement1 or statement2 if
expression is true or false, respectively. A variant of this
conditional statement is
if
expressionthen statement
with no else component. In this form, the statement is
executed only if expression is true. The conditionals can
be nested as the following general form.
if
else
expression1 then statement1
if
expression2 then
statement2
else
if
expression3 then
statement3
else
...
30
Looping Constructs
A program in an imperative programming
language usually performs its task by executing
a sequence of elementary steps repeatedly.
– Looping constructs can be classified into two
categories as the following:
– Definite iteration loops in which the number of
iterations is predetermined when control flows
through construct.
Indefinite iteration loops in which the number of
iterations is not known when control flows
through construct.
31
Looping Constructs
In other words, the looping constructs are
divided depending upon whether or not we can
predict the number of times the loop will be
executed. The syntax for indefinite iteration
construct is as the following
while conditional-expression
do statement
where the control flows through the execution of
the statement which is called the body of the
construct as soon as the conditional-expression
is evaluated to true.
32
Looping Constructs
In the following the loop is executed with index
taking the values 0, 1, 2, … , 9 in order to
initialize the corresponding array element with
zero.
index  0;
while (index  10)
{
A[index]  0;
index  index  1;
}
33
Looping Constructs
The syntax for definite iteration construct is as the
following
for
(expression1;
expression2;
expression3)
body-of-loop;
where control flows through the execution of the body-ofloop depending upon the value of the expression2. For
example, in the following
for
(index  0; index  10; index)
A[index]  0;
the assignment A[index]  0; is executed with index
taking the values 0, 1, 2, … , 9 on successive
executions.
34
Unions
We can construct a union of two types, in which
can be formed by taking the set theoretic union
of their sets of values. Union type come in two
varieties.
– Discriminated Unions: If a tag or discriminator is
added to each element field to distinguish which type
the element is.
– Undiscriminated Unions: It lack the tag, and
assumptions must be made about the type of any
particular element.
A language adopted discriminated and
undiscriminated union is Modula-2, and C is a
language adopted undiscriminated union.
35
Functions and Procedures
Despite their distinct roles as operators and actions,
functions and procedures are very similar to each other.
The only difference between them is that functions return
a result and procedures do not. No wonder one
language such as C calls them both as functions and
another language such as Modula-2 calls them both
procedures. In general, a procedure or function
declaration defines the components as:
– A name for the declared procedure or function.
– The formal parameters which are placeholders for input and
output.
– A body consisting of local declarations and statements.
– An optional result type to address the data type of the returned
result.
36
Control Flow
Statement-Oriented
Block-Oriented
– Localize changes
– Correct assumptions at the beginning and the
end
– No need to concern about conflicts names
– Facilitate organization of a program
– Explicit control information to descendant
blocks
37
Figure 6.1 Control
flows through a
sequence of statements
© 2003 Brooks/Cole Publishing / Thomson Learning™
38
Figure 6.2 Control
flows through a block
© 2003 Brooks/Cole Publishing / Thomson Learning™
39
Execution Steps for Imperative
Programming
An imperative program goes through
several stages in order to be executed.
– Compilation-linking-loading-execution
Compilation: This process translates the
program into a relocatable object code,
meaning that the program and all
individual subprograms are translated into
an intermediate code compatible with the
computer system.
40
Execution Steps for Imperative
Programming
Linking: This process incorporates the necessary
libraries into the program. These libraries
associated with subprograms are already
programmed, debugged and compiled. The goal of
linking process is to obtain a relocatable object
code containing of all parts of the program
including all library references satisfied.
Loading: This is the process that a relocatable
code is placed in memory for execution. This
process requires converting relocatable object code
to absolute format for execution.
Execution: This is the process that the control
flows through the execution of the statements of the
program.
41
© 2003 Brooks/Cole Publishing / Thomson Learning™
Figure 6.3 The Compile-Link-LoadExecute steps of program execution
42
Undesirable Characteristics for
Imperative Programming
Difficulty in Reasoning
– The correctness of a program depends on the
contents of each and every memory location.
Referential Transparency
Side Effects
Indiscriminate Access
Vulnerability Access
No Overlapping Definitions
43
Referential Transparency
A system is referentially transparent if the meaning of the
whole can be determined solely from the meaning of its
components.
For example, in a mathematical expression such as
F(x)G(x), we may substitute another function H for F, if
we know that it produces the same values as F.
In imperative programming languages we are not
assured of this, because the meaning of the expression
depends on the history of computation of the
subexpressions.
Indeed, if F or G change the value of their parameter or
modify some global variables, we are not even certain
that F(x)G(x)  G(x)F(x).
44
Side Effects
Imperative programming supports imperative operations
that are performed not to obtain a value but rather for
some side effect on the computations.
For example, operators like  or  in C language have
side effects, meaning that besides returning a value they
also modify an underlying variable.
Side effects can be convenient but they can also cause
trouble, because the actions of retrieving the value and
updating the variable might not happen at the same time,
one at the time of expression evaluation and one at the
termination of the execution.
45
Side Effects
Consider
– class_list[index]  class_list[index]  0;
In this statement, the intent is to store zero
at the two consecutive positions in array.
But depending on when index is updated,
a position in array could be skipped and
index might be increased only by 1.
(sideeffect2.cpp)
46
Indiscriminate Access
The problem of indiscriminate access is
that, programmers can not prevent
inadvertent access to variables or data
structures.
For example, suppose we want to provide
a stack to be used in an Algol program.
We would probably design the program as
the following.
47
An example of Algol
begin
integer array
S[1:100];
integer TOP;
procedure
PUSH(x);
integer x;
begin
TOP  TOP1;
S[TOP]x;
end;
procedure
POP(x);
integer x;
begin
POP  S[TOP];
TOP  TOP1;
end;
TOP 0;
...
uses of PUSH and POP
end;
...
48
Explanation
In this case, the variable S which is the stack
must be declared in the same block as PUSH
and POP so that it is visible from the bodies of
PUSH and POP procedures.
In addition, for the PUSH and POP procedures
to be visible to their users, they must be
declared in a block that contains all users. In this
example, there is no guarantee that all users of
the stack go through the PUSH and POP
procedures in order to manipulate the data
structure.
49
Vulnerability Access
The problem of vulnerability access is that, under certain
circumstances it is impossible to preserve access to a variable.
Consider a very large Algol program.
begin
integer
x;
. . . . many lines of code
begin
. . . . many lines of code
x  x  1;
. . . . many lines of code
end;
. . . . many lines of code
end;
In this example, we are supposing that there are so many lines
of code between the definition and use of x variable. Let’s
further suppose that in the process of maintaining this program
50
we decide that we need a new local variable in the inner block.
Change a little bit
If we pick x without realizing that it was already used in that block, it results
the following program.
begin
integer
x;
. . . . many lines of code
begin
real x;
. . . . many lines of code
x  x  1;
. . . . many lines of code
end;
. . . . many lines of code
end;
In this case, with this modification access to the outer declaration of integer x
has been blocked and the statement x  x  1 now refers to the new real
variable x. In other words, the new declaration of x has been interposed
between the original definition of x and its use. Thus, the problem of
vulnerability access is the inability to preserve access to a variable.
51
No Overlapping Definitions
The problem of no overlapping definitions
is the inability to control shared access to
variables.
As a consequence, it creates both a
maintenance and security problem.
52
© 2003 Brooks/Cole Publishing / Thomson Learning™
Figure 6.4 Overlapping structures in a program
53
Desirable Characteristics for
Imperative Programming
Some of the issues involved in good
programming can be outlined.
No Implicit Inheritance
– The default should not be to extend the scope of a
variable to inner block.
Distinguish Access to Data Structures
– It should be possible to distinguish different data
types of access.
Decoupling Declarations
– Declaration of definition, name access and allocation
should be decoupled.
54
Desirable Characteristics for
Imperative Programming
Natural Form for Expressions
– Write expressions as you might make them
understanding.
if (!(block_id<block_1)||!(block_id>=block_2))
or
if ( (block_id>=block_1)|| (block_id<block_2))
Which is better?
55
Parenthesize to Resolve
Ambiguity
Parentheses specify grouping and can be
used to make the intention more clear
even when they are not required.
– if (*&MASK ==BITS)
– if (*&(MASK ==BITS))
– if ((*&MASK) ==BITS)
– if (MASK ==BITS)
56
Break up Complex Expressions
– The statement is more easy to understand
when is broken into several pieces.
*ptra+=(*ptrb = (2*index<(index_1-index_2)?
array1[index++]:array2[index--])
If (2*index<(index_1-index_2)
*ptrb=array1[index++];
else
*ptrb=array2[index--];
*ptra+=*ptrb;
57
Summary
Taxonomy
Design Principles
Control constructs
Execution steps
Undesirable Characteristics
Desirable Characteristics
58