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 colors’blue (like Ada) or any other syntax that you might choose. Such clarification wouldn’t 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] it’s 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 argument’s 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 doesn’t 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 I’ve 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;