CS 334
Programming Languages
Spring 2002

Solutions to Assignment 9
Due Thursday, 5/2/2002


  1. Please do problem 10.32 on page 10-46 of the text. To see what the problems are with multiple inheritance in C++, please read problem 10.16 on page 10-44. In particular, think about the ambiguities and arbitrary decisions that C++ must make in order to solve the problem stated illustrated in 10.16, and show that these ambiguities do not arrise in Java with implementation of multiple interfaces.

    Solution: There is no problem with multiple interfaces because no code is associated with features listed in interfaces. Even if a method m is listed in two different interfaces, there is no problem. If the signatures of m are identical, then they represent the same method (and no conflicting bodies exist). Otherwise they represent different overloaded functions (and again there is no conflict). The only possible problem is if two interfaces give the same method m with the same parameter types and different return types. But in that case, this is simply a static error.

  2. Variables can be said to have both L-values and R-values, where the L-value of a variable is the location it denotes and the R-value is the value stored in that location. The environment keeps track of the locations corresponding to variables, while the store (or state) keeps track of the values stored in locations. The semantics of the assignment statement for most programming languages is usually given as follows: When V := E is executed, the L-value of V is first obtained from the environment (call it l), then the expression E is evaluated, and that resulting value is stored in the location l. While side-effects can create great confusion in terms of what the result of an assignment statement will be; in this problem, side-effects are not the problem!

    a) Describe in detail the effect of executing the Pascal assignment

    	a[a[i]] := a[i] + 1
    	

    Solution: First a[i] is evaluated to get the integer, n, stored in a[i]. Then the location corresponding to a[n] is calculated and remembered. Then a[i] is evaluated again and the value is added to one, giving an integer value m (really n+1). That value is stored in the location of a[n].

    b) Is it always the case (in Pascal) that the value of L after executing assignment L := E is equal to the value of E before executing the command? I.e. if you execute the following code:
    	write(E); L := E; writeln(L);
    
    will the two values printed always be the same? For the purposes of this problem assume that the evaluation of E has no side effects. Hint : consider the assignment in part (a) with i = 2, a[2] = 2, a[3] = 0.

    Solution: The hint shows that the answer is no! a[a[i]] = a[2], so a[2] gets 2+1 = 3. But now the value of a[a[i]] is the value of a[a[2]] = a[3] which is 0. Thus write(E) gives 3, but writeln(L) gives 0.

    c) What does this tell you about the validity of the axiomatic rule for assignment,{P [E / L]} L := E {P}, (e.g., if L = a[a[i]], E = a[i] + 1, and P is a[a[i]] = 3)? Suggest a restriction which makes it valid.

    Solution: There are any number of restrictions that make the rule valid. The simplest, but most restrictive, is to say the rule is not valid with arrays. A less severe restriction is to say the rule is invalid if the left-hand side might be affected by the evaluation of E and the assignment. In the example above, the evaluation of E is fine, but the assignment itself changes the value of the left-hand side. a[i] = i++ is another problematic statement because a[i] before evaluating the right side will refer to a different location than a[i] after the evaluation of the right side. Thus the problem can arise from different aspects of the evaluation of E and assignment. A simpler to phrase restriction might be to only allow the rule to be applied when the subscript of an array on the left-hand side is a constant, though this is still pretty restrictive. Personally I'd probably restrict the subscript to not include references to the same array and not to allow any side effects that would change the value of the subscript.

  3. Use the formal rules for axiomatic semantics given in the lecture notes to prove the correctness of the following program:
    {Precondition: n > 0}
       i <- n
       fact <- 1
       while i > 0 do
       {assert:  ...}
          fact <- fact * i
          i <- i - 1
       end while
    {Postcondition:	fact = 1*2*...*n}
    Hint: You need to figure out the loop invariant before you can complete the proof. Your proof should take the same form as the one in the lecture notes - hand-waving is NOT acceptable. Be sure to prove the entire algorithm is correct with respect to the precondition and postcondition. The lecture notes example only includes showing the loop invariant is correct. You must prove the entire program correct with respect to the precondition and postcondition!

    Note: If you prefer to use the weakest precondition rules in the text, be my guest, but I suspect you will find it easier to use those given in class instead.

    Solution:

    The key is to find the loop invariant. Note that it will have to be strong enough to prove the postcondition. Let's try:

       fact * i! = n! & i ≥ 0

    Recall that the correctness rule for while loops is:

    if we can show {P & B} C {P}, then {P}while B do C {P & not-B}

    Since B is "i > 0", we must show that:

       {fact * i! = n! & i ≥ 0 & i > 0}
          fact <- fact * i
          i <- i - 1
       {fact * i! = n! & i ≥ 0}
       

    Now we can use the assignment rule ({P[e/x]} x := e {P}) twice by working our way up the program:

       {fact * (i-1)! = n! & i -1 ≥ 0} 
          i <- i - 1 
       {fact * i! = n! & i ≥ 0}
    

    and then

       {fact * i * (i-1)! = n! & i -1 ≥ 0} 
          fact <- fact * i 
       {fact * (i-1)! = n! & i -1 ≥ 0}
       

    Note that the precondition of the last statement can be rewritten as:

       {fact * i! = n! & i  ≥ 1}

    By Composition, we get:

       {fact * i ! = n! & i  ≥ 1} 
          fact <- fact * i;
          i <- i - 1 
       {fact * i! = n! & i ≥ 0}
       

    But clearly, fact * i! = n! & i ≥ 0 & i > 0 implies fact * i ! = n! & i ≥ 1. Therefore by the consequence rule we get:

       {fact * i! = n! & i ≥ 0 & i > 0} 
          fact <- fact * i; 
          i <- i - 1 
       {fact * i! = n! & i ≥ 0}
       

    Since this establishes that fact * i! = n! & i ≥ 0 is a loop invariant, the while rule implies:

       {fact * i! = n! & i ≥ 0}
          while i > 0 do  
            fact <- fact * i
            i <- i - 1
          end while
       {fact * i! = n! & i ≥ 0 & not(i > 0)}

    Now i ≥ 0 & not(i > 0) implies i = 0 and therefore fact * i! = fact * 0! = fact.

    Thus fact * i! = n! & i ≥ 0 & not(i > 0) implies fact = n!.

    Again by the consequence rule (and our earlier deduction on pre- and post-conditions on the while loop) we can now deduce:

       {fact * i! = n! & i ≥ 0}
          while i > 0 do
             fact <- fact * i
             i <- i - 1
          end while
       {fact  = n!}
       

    All we have to worry about now is taking care of the program before the while loop.

    Thus if we can show:

       {n > 0}
          i <- n 
          fact <- 1
       {fact * i! = n! & i ≥ 0}
    then we can glue this together with the above correctness clause (using the composition rule) to show the entire program is correct.

    We can prove this last part as before by pushing the postcondition up through the assignment statements using the assignment rule as follows:

       {1 * i! = n! & i ≥ 0}
          fact <- 1 
       {fact * i! = n! & i ≥ 0}
       
    and
       {1 * n! = n! & n ≥ 0}
          i <- n 
       {1 * i! = n! & i ≥ 0}
       
    Since 1 * n! = n! is clearly true we can simplify this statement to:

       {n ≥ 0} 
          i <- n 
       {1 * i! = n! & i ≥ 0}
       
    By sequencing we get:
       {n ≥ 0} 
          i <- n; fact <- 1 
       {fact * i! = n! & i ≥ 0}
       

    We now notice that the precondition we were looking for was {n > 0}, but of course, n > 0 implies that n ≥ 0. Thus again by consequence, we get

       {n > 0} 
          i <- n; fact <- 1 
       {fact * i! = n! & i ≥ 0}
       

    (This should make intuitive sense - if we strengthen the precondition, the postcondition should still be true.)

    Using sequence, we can put this partial correctness assertion together with that for the while loop and obtain:

       {Precondition: n > 0}
          i <- n
          fact <- 1
          while i > 0 do
             fact <- fact * i
             i <- i - 1
          end while
    	  {Postcondition:fact = 1*2*...*n}

    as desired!

  4. Do problem 13.14 on page 13-32 of the text using the "small-step" operational semantics given in the text. You may work just with an environment (no state) as the book does, or may use an environment and state as we did in class with the big step semantics. Please show every step of the reduction.

    Solution omitted

  5. Do problem 13.15 on page 13-32 of the text.

    Solution omitted