|
|
Dynamic Referencing Environment Of a Procedure
The dynamic ancestors of a
procedure p() are all the procedures called before p() during
the execution of a program, that have not yet finished executing. These are
the procedures that are waiting for procedure p() to finish executing
before they can terminate. The dynamic non-local referencing
environment of a procedure p() is the set of all the unique
variables declared in the dynamic ancestors of p(), that have not been
re-declared in the procedure p(). When more than one dynamic ancestor
of p() declares a variable not re-declared in p(), the variable
included in the non-local referencing environment of p() is the one
declared in the procedure closest to p() in the sequence of procedure
calls in the program. Just as in the case of dynamic scope, the dynamic referencing
environment of a procedure in a program can be determined by studying the
sequence in which procedures are called during execution of the program.
Unlike static referencing environment,
dynamic referencing environment cannot be determined merely by studying the
text of a program. Pascal uses static scope, not dynamic
scope. For purposes of discussing dynamic referencing environment, we will
assume that the language we are considering is a Pascal-like language,
and not Pascal itself. However, using the same Pascal syntax for all the
modules in the courseware will help us focus on the concept of referencing
environment in this module. What is the dynamic non-local
referencing environment of the procedure initialize() in the following
program, assuming that procedures are called in the following order: main() calls compute(),
which in turn calls transform(), which in
turn calls initialize()? program main var y: Real; procedure compute() var x : Integer; var z : Integer;
procedure initialize()
var z: Integer;
begin {initialize} ... end {initialize} procedure transform() var x: Real; begin {transform} ... end {transform}
begin {compute} ... end {compute} procedure print() var y : Integer;
begin {print} ... end {print} begin {main} ... end {main} Answer: The variable z declared in the
procedure initialize() is part of its local, not non-local
referencing environment. The dynamic ancestors of initialize()
are the procedures transform(), compute() and main().
The procedure print() is not in the sequence of current procedure
calls - either it has not yet been called, or it has already terminated
execution. Therefore, it is not a dynamic ancestor of initialize(). The variable x declared in transform()
is a part of the dynamic non-local referencing environment of initialize()
since initialize() does not re-declare x. The variable x declared in compute()
is not a part of the dynamic non-local referencing environment
of initialize() since x is re-declared in the procedure transform(),
which is closer to initialize() than compute() in the sequence
of procedure calls. The variable z declared in compute() is
again not a part of the dynamic non-local referencing environment
of initialize() since z is re-declared in the procedure initialize(). The variable y declared in main()
is a part of the dynamic non-local referencing environment of initialize()
since neither initialize() nor any dynamic ancestors of initialize()
already considered (transform(), compute()) re-declares y.
In a dynamically scoped language, the
concept of referencing environment is essential to understanding programs. In
the above example, you would not be able to correctly answer the following
questions if you did not understand the concept of referencing environment: 1. If the procedure initialize() uses an expression x + y
+ z, which x, y and z does it refer to? 2. If we introduce a new variable x in initialize(),
how might that break the code in initialize() that worked correctly
before its introduction? The answers to these questions depend on
the sequence in which procedures are called during the execution of the
program. How would the answers differ for each of the following sequences of
procedure calls? 1. main() calls compute(), which in turn calls transform(), which in turn calls initialize(). 2. main() calls compute(), which in turn calls initialize(). 3. main() calls print(), which in turn calls compute(), which in turn calls transform(),
which in turn calls initialize(). Can you answer these questions? Procedures may be called in a different
order each time a program is executed. In other words, the order in which
procedures are called in a program is unpredictable. Therefore, the
referencing environment of a procedure in a program written in a dynamically
scoped language is unpredictable. This makes it hard to verify the
correctness of such programs. (Can you give an example from the above
program?) Therefore, dynamic scope is not as popularly used in programming
languages as static scope. Most programming languages require that
the data types of variables used in an expression are compatible with one
another. In this context, “dynamic scope is said to clash with static typing
of variables”. Can you explain this statement? Algorithm
to determine Dynamic Referencing Environment: In order to determine the dynamic
referencing environment of a procedure in a program, it is convenient to use
a directed graph of the order in which procedures are called during the
execution of the program. The directed graph for the earlier program is shown
below:
Algorithm to
determine the dynamic referencing environment of a procedure p() using a
directed graph of procedure calls: 1.
Highlight the directed graph from
p() up to the start of the graph, i.e., main(). On this path
lie all the procedures which serve as dynamic ancestors of p() during
the execution of the program. 2. Start from the procedure that called p() and work your
way up to main(). In each procedure, identify all the variables
declared in the procedure that have not been re-declared either in p(),
or in the dynamic ancestors of p() that you have already considered.
Include these variables in the non-local referencing environment of p().
Stop when you have considered main() in this fashion. The directed graph of procedure calls is
shown again below. In the graph, the dynamic ancestors of the procedure initialize()
have been numbered in the order in which they would be considered by the
algorithm.
Test
your understanding - Instructions: In order to better understand the concept of dynamic referencing environment of a procedure, solve problems using the accompanying problet. The problet will present you with the outline of a Pascal-like program and the order in which procedures are called during its execution. It will ask you to identify all the variables that are in the non-local referencing environment of a selected procedure, as well as the procedures where these variables were declared. 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: 1. Drawing boxes to delineate definitions of procedures in the program; 2. Changing the amount of indentation to set off different procedure definitions in the program. Follow these steps to solve each problem (See Figure below): 1. Study the Pascal-like program 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 the Dynamic Referencing Environment Problet. |