COEN 171
Principles of
Programming Languages
Winter 2000
Homework 2 Answers
Page 189 ff:
8. Assume a Pascal
program with nesting structure
Program main
Var x: integer;
Procedure sub1;
Var x:
integer;
Procedure
sub2;
End;
End;
Procedure
sub3;
End;
End;
And the invocation
order main, sub1, sub2, sub3.
a. Using static
scoping, which x is referenced in
sub1 the x
declared in sub1
sub2 the x
declared in sub1
sub3 the x
declared in main
b. Using dynamic scooping,
which x is referenced in
sub1 the x
declared in sub1
sub2 the x
declared in sub1
sub3 the x declared in sub1
10. Consider the
following Pascal program skeleton
program main
var x, y, z: integer;
procedure sub1;
var a, y, z:
integer
procedure
sub2;
var
a, b, z: integer;
end;
end;
procedure sub3;
var a, x, w:
integer;
end;
end;
List all of the
variables, along with the program units where they are declared, that are
visible in the bodies of sub1, sub2, and sub3, assuming static scoping.
Use the notation
programunit.variablename. In sub1, sub1.a, sub1.y, and sub1.z are visible
(local variables are always visible), and main.x is also visible (main.y
and main.z are not visible since y and z were redefined in sub1). In sub2,
sub2.a, sub2.b, sub2.z, sub1.y (a and z have been redefined by sub2), and
main.x (y has been redefined by sub1) are visible. In sub3, sub3.a, sub3.x,
sub3.w, main.y, and main.z (x has been
redefined by sub3) are visible.
12. Consider the following
C program skeleton
void fun(void) {
int a, b, c; /* definition 1 */
while (
) {
int b, c,
d; /* definition 2 */
/* point 1 */
while (
) {
int
c, d, e; /* definition 3 */
/* point 2 */
}
/* point 3 */
}
/* point 4 */
}
For each of the
marked points, list the variables visible, and the definition statements that
defined them
Point 1: 2.b, 2.c, 2.d, 1.a
Point 2: 3.c, 3.d, 3.e, 2.b, 1.a
Point 3: 2.b, 2.c, 2.d, 1.a
Point 4: 1.a, 1.b, 1.c
Page 255 ff:
2. Why does a
decimal value waste memory?
Decimal (more
formally, binary coded decimal) values store numeric information as digits
encoded using the four bit binary equivalents: 0 (0000) to 9 (1001). That means
a single byte can hold values between 0 and 99. But simply using the same byte
to hold a binary value will yield values between 0 and 255 (or 128 and +127).
13. Suppose a
language includes user-defined enumeration types and that the enumeration
values could be overloaded; that is, the same literal value could appear in two
different enumeration types, as in:
type
colors = (red, blue,
green);
mood = (happy, angry,
blue);
Use of the constant
blue cannot be type checked. Propose a method of allowing such type checking without
completely disallowing such overloading.
Essentially, the
method must allow the compiler to disambiguate any references to blue. That
could be done by requiring the type name to be associated with the enumeration
literal when there may be any confusion. For example, colors.blue or
colorsblue (like Ada) or any other syntax that you might choose. Such
clarification wouldnt be necessary to enumeration literals that were unique.
Assume double
precision reals are stored in 64 bits on a machine where each address
represents a byte. What is the offset for element A[4, 7], where A is a
2-dimensional array declared
A: array [-1..5,
6..9] of double;
if the array is
stored in
(a) row major
order?
Row major storage
stores all the elements of the first row, then all of the second row, etc. To
get to a particular array element A[i,j] its necessary to skip over all the
memory locations storing rows ahead of the one containing the element you want
(for A[4, 7] we have to skip over rows with first subscript values 1, 0, 1, 2,
and 3). That gives you the offset to the row. Then you need to take the offset
within the row to the element desired.
In general, a
two-dimensional arrays is declared by specifying a range of possible values for
each subscript. Ranges have lower bounds (lb) and upper bounds (ub). For the
specific array in this problem, the lower bound for the first subscript is 1,
and for the second subscript is 6. The upper bound for the first subscript is 5
and for the second subscript is 9.
So to get to A[s1,
s2], the number of rows to skip is (s1 lb1) and each row contains (ub2 lb2
+1) elements. The offset within the desired row is (s2 lb2). Then the offset
in number of elements is
(s1 lb1)*(ub2-lb2 +1) + (s2 lb2)
This needs to be
multiplied by the number of memory addresses each element occupies (for this
example, 8).
The final answer is
Offset = [(4 (-1)) * (9 6 + 1) +
(7 6)] * 8 = [5 * 4 + 1] * 8 = 168
(b) column major
order?
Similar reasoning here,
except to find the element you must skip over a number of columns to get to the
column containing the element you want, then get the offset within the column
to reach the element. The general formula is
[(s2 lb2) * (ub1 lb1 + 1) + (s1
lb1)] * (size of each element)
or for our example
Offset = [(7 6) * (5 (-1) + 1) +
(4 (-1))] * 8 = [1 * 7 + 5] * 8 = 12 * 8 = 96
Page 285 ff:
14. Let the
function FUN be defined as
function FUN (var K: integer) :
integer;
begin
K := K + 4;
FUN := 3 * K
1 {return function value}
End;
Suppose FUN is used
in a program as follows:
I := 10;
SUM1 := (I / 2) + FUN(I);
J := 10;
SUM2 := FUN(J) + (J / 2);
(a). What are the
values of SUM1 and SUM2 if the operands in the expressions are evaluated left
to right.
Because the
parameter of FUN is a var parameter, any changes to its value in FUN also cause
the same changes to the arguments value in the invoking program unit. So I and
J have their values changed. In this case, if X has value 10 FUN(X) returns a value of 41 and sets X to
14. So SUM1 := (10/2) + 41, or 46. SUM2 := 41 + (14/2), or 48.
(b). What are the
values of SUM1 and SUM2 if the operands in the expressions are evaluated right
to left.
Same thing here, except
SUM1 := (14/2) + 41 = 48, and SUM2 := 41 + (10/2) = 46.
15. Let the C
function fun be defined as
int fun (int *k) {
*k += 4;
return 3 * (*k) 1;
}
Suppose fun is used
in a program as follows:
Void main () {
Int i = 10, j = 10,
sum1, sum2;
sum1 = (i/2) + fun
(&i);
sum2 = fun (&j) +
(j/2);
}
Determine the
values of sum1 and sum2 by running the program on a computer. Explain your
results.
Since fun has the
side effect of increasing its argument by 4, order of evaluation determines
what the result is. If the compiler evaluates operands left to right, sum1 ==
46 and sum2 == 48; if right to left, sum1 == 48 and sum2 == 46; if
parenthesized subexpressions are evaluated first, both sum1 and sum2 == 46; if
functions are evaluated first, both sum1 and sum2 == 48.
Page 325 ff:
10. Rewrite the
following code segment using a loop structure in the following languages
k := (j + 13) / 27
loop:
if k > 10 then goto
out
k := k + 1 ; NOTE loop control
variable incremented before arithmetic
I := 3 * k 1
goto loop
out:
a. Pascal
Pascal allows
expressions in the initial value of the for loop, deals with initial and final
values that cause the loop body to never be executed, but doesnt allow the loop
control variable to be changed in the loop body.
For k := ((j + 13) / 27) to 10 do
i := 3 * (k + 1) 1;
b. FORTRAN 77
Fortran 77 allows
expressions for the initial value of the DO, and correctly handles zero
iteration loops
DO 100 K = (J + 13) / 27, 10
I = 3 * (K + 1) 1
100 CONTINUE
c. Ada
The formal Ada
syntax allows expressions as the initial value for a loop, but Ive never done
that. I would probably code this using a temporary variable to hold the initial
value.
temp := (j + 13) / 27;
for k in temp .. 10 loop
i := 3 * (k+1) 1;
end loop;
d. C, C++, or Java
You can do
anything.
for (k = (j + 13) / 27; k < 10;
k++)
i = 3 * (k + 1) - 1
16. Rewrite the
following Pascal case statement using only two-way selection.
Case index 1 of
2, 4: even := even + 1;
1, 3: odd := odd + 1;
0: zero := zero + 1;
else error := true
end;
My solution
compares against index rather than index 1, to avoid unnecessary arithmetic
at run time
if (index = 3) or (index = 5)
then even := even + 1
else if (index = 2) or
(index = 4)
then odd :=
odd + 1
else if
(index = 1)
then
zero := zero + 1
else
error := true;