Static Scope of a Procedure Name
The static scope of the name of a procedure determines all the procedures/functions that can call the procedure.
The static scope of the name of a procedure p() in a Pascal program is defined to include:
1. The procedure p() itself;
2. All the procedures defined directly within p();
3. The procedure within which p() is defined, say f();
4. All the procedures defined after p() within f(), and in turn, all the procedures defined within them either directly or through other procedures.
A more useful concept is the converse of the static scope of the name of a procedure, which we refer to as the callability of the procedure. Callability of a procedure determines the procedures that it can call.
A procedure in a Pascal program can call:
1. Itself. This is a recursive call.
2. All the procedures defined directly within it.
3. All the procedures within which its definition is nested, either directly, or through other procedures. These procedures are called its static ancestors. The only exception is main(), which cannot be called by any procedure.
4. All the procedures defined before it in the program, and directly within one of its static ancestors.
Admittedly, Pascal is one of the languages with the most complex rules for procedure callability. Since Pascal is a statically scoped language, we can determine the callability of a procedure in a Pascal program by studying only the text of the program.
What are the procedures that compute() can call in the following Pascal program?
The procedure compute() can call itself recursively.
It can call the procedures initialize() and transform(), since they have been defined directly within it. However, it cannot call the procedure defaults(), since defaults() is defined within initialize(), and not directly within compute().
It cannot call main(), even though main() is a static ancestor of compute(). No procedure can call main().
It can call the procedure read(), since read is defined before compute(), and directly within main(), which is a static ancestor of compute(). However, it cannot call the procedure prompt() even though prompt() is define before compute(), because prompt() is defined within read(), and not directly within a static ancestor of compute() (in this case, main()).
It cannot call the procedure print(), since print() is defined after compute() in the program.
Procedure callability is essential for writing large Pascal programs. In the above example, you would not be able to correctly answer the following questions if you did not understand the concept of procedure callability:
1. Suppose we want to introduce a new library procedure/function into the program called threshold(), such that both prompt() and transform() procedures should be able to call it, where should we insert the procedure into the program?
2. Suppose we revised the program and the need arose for the procedure read() to call defaults(). Can read() call defaults()? If not, how do we relocate the procedure defaults() without affecting any of the procedures that can currently call it?
Can you answer these questions?
Clearly, Pascal programmers must carefully plan the structure of large programs – the placement of procedure definitions in the program and within each other - in order to manage the callability of procedures. Modifying the structure of a Pascal program is not easy. This is one of the reasons why Pascal did not gain the acceptance of the computing industry as the programming language of choice for developing large-scale programs.
Once you have answered the above questions, consider the effect of relocating a procedure, on its referencing environment. This further illustrates why allowing procedure definitions to be nested in a language greatly complicates the programs written in the language. Therefore, most current languages (e.g., C++, Java) do not permit definitions of procedures/functions to be nested.
The structure of a Pascal program, in particular, the order in which procedure definitions are nested in the program can be graphically depicted as a tree. This tree is called the static tree of the program. It is quite convenient to use the static tree of a Pascal program to determine the callability of procedures in the program.
Algorithm to draw the static tree of a Pascal program:
1. Draw the main() program as the root node of the tree.
2. Identify all the procedures/functions directly defined in main(). Draw them as child nodes of main() node.
3. Repeatedly carry out step 2 for all the leaf nodes of the tree, until there are no more nested procedures in the program.
In the Pascal program presented earlier, the procedures read(), compute() and print() are defined directly in main(), and are its children. The procedure prompt() is defined within read(), and is its child. The procedures initialize() and transform() are defined within compute() and are its children. The procedure defaults() is defined within initialize(), and is its child. The static tree of the program is shown below.
Algorithm to determine the callability of a procedure p() using the static tree of the program:
A procedure can call any procedure that it can reach by:
· climbing up zero or more levels of the tree;
- with the exception of main(), which cannot be called by any procedure
· and climbing down at most one level;
- and only to the left of any path by which it climbed up.
Let us apply this graph algorithm to the earlier problem of determining the procedures that compute() could call.
· compute() can reach itself by climbing up zero levels. Therefore, it can call itself.
· compute() can reach initialize() and transform() by climbing up zero levels and climbing down one level. Since no climbing up was involved, the restriction that climbing down should be only to the left does not apply. Therefore, compute() can call the procedures initialize() and transform().
· Although compute() can reach main() by climbing up one level, it cannot call main(). main() is an exception to the rule.
· compute() can reach read() by climbing up one level and climbing down one level to the left of the path it used to climb up. Therefore, it can call read(). However, it cannot call print(), since, in order to reach print(), it would have to climb down to the right of the path it used to climb up the tree.
· compute() cannot reach prompt() or defaults() without climbing down more than one level. Therefore, it cannot call these procedures.
The static tree of the program is shown again below. All the paths used by compute() to determine the procedures it can reach, and hence call are highlighted.
In order to better understand the concept of callability of procedures in Pascal, solve problems using the accompanying problet. The problet will present you with the outline of a Pascal program, and ask you to identify all the procedures in the program that a particular procedure can call. You may solve the problems in one of two ways:
1. You may study the program and answer the problem. In order to help you read the program, the problet provides a Format menu with options such as:
a. Using different colors to indicate the different levels of nesting in the program;
b. Drawing boxes to delineate definitions of procedures in the program;
c. Changing the amount of indentation to set off different procedure definitions in the program.
2. Alternatively, you may study the static tree of the program and answer the problem. Choose the View menu to display the static tree of the program.
Follow these steps to solve each problem (See Figure below):
1. Study either the Pascal program or its static tree in the left panel.
2. Read the question posed in the top right panel.
3. Indicate your answer in the mid-right panel, and click on Check My Answer button.
4. Study the detailed feedback provided by the problet in the bottom right panel. If your answer is incorrect, this panel will explain why it is incorrect.
5. Click on Get Another Problem button to generate another problem.
Are you ready? Click here to launch