CSC447: Concepts of Programming Languages

Answers to Homework Assignment 3, Fall 1996


Exercise 1: Consider the following procedure swap2.

  procedure swap2 (x, y : integer);
    procedure f() : integer;
      var z : integer;
    begin (* f *)
      z := x; x := y; return z;
    end;
  begin (* swap2 *)
    y := f();
  end;

Describe the effect of the procedure call swap2(i, A[i]) under each of the following parameter passing methods:

Answer:

a. Call-by-value: no effect
In the procedure swap2(), x will be assigned the value of i, and y will be assigned the value of A[i]. When procedure f() is called, the values of x will be changed to the value of y. When f() returns, it will be changed to the value of x. However, when the procedure swap2() returns, the values of i and A[i] will be unchanged.

b. Call-by-reference: the values will be swapped
Instead of copying values as in (a) when we call swap2() by reference, x "points" to the memory address which i is located, and y "points" to the memory address where A[i] is located. Thus, any change in x will immediately change i, and any change in y will immediately change A[i]. Note -- this will cause harmful effects on A[i], by putting i out of range, however, upon return from swap2(), and immediate reference of A[i] may not give you what you think, and may cause runtime errors. So, at z=x, z becomes what i is, and at x = y, i becomes what A[i] is, and at return z, y becomes what z is (and what i was). When we return from the function, i and A[i] (old i) are successfully swapped.

c. Call-by-value-result: the values will be swapped
This will have the same results as (b) except x and y are copies of the values of i and A[i] respectively and contain their own memory. The values of these two are successfully swapped, and on completion of swap2(), x is copied into i and y is copied into A[i] (old i).

d. Macro expansion: Unpredictable
Has unknown results due to the expansion happens as each line is reached. The piece of code which has the statements
"z = x; x= y; return z;" will expand to x=i; i=A[i] and return i; the line "y = f()" becomes "A[i] = f()", but i now has changed to whatever A[i] was storing, which could put the array index out of bounds. At best some new A[i] array element will change, but the original A[i] will remain unchanged.

Exercise 2:

Consider the following program and describe the effect of running the program with both lexical scoping and dynamic scoping.

  program main;
    var x : integer;
    procedure sub1;
      begin
        writeln ('x=', x)
      end;
    procedure sub2;
      var x : integer;
      begin
        x := 10;
        sub1;
      end;
    begin
      x:= 5;
      sub2;
    end.

Answer:

With lexical scoping, the program will print 'x=5' and with dynamic scoping the program will print 'x=10'.

Exercise 3: Describe the stack contents (the activation records) for the following program at the point where pred(2) is called.

program activations;
  function pred (m: integer) : integer;
    begin pred := m-l; end;
  function f (n: integer): integer;
  begin
    if n = 0 then 
        f := 1 
    else 
        f := n * f (pred(n))
  end;
begin
   f(3);
end.
Answer: 
activation record for f(3): arg = 3
                            PC (state info)
activation record for f(2): arg = 2
                            PC (state info)
activation record for p(2): arg = 2 

Exercise 4: Write and compile the "hello world" program on your favorite C++ compiler. Submit the source code here.

Answer:

#include <iostream.h>

main ()
{
cout << "hello world\n";
return 0;
} 

Exercise 5: Modify the stack program that we described in class to implement a first-in/first out buffer, based on the following invariants:

Answer:

#include <iostream.h>
#define STACK_SIZE   5        // stack size

//********************************************************************
// Class: Stack
//********************************************************************
class stack{
protected:
   int buffer[STACK_SIZE];    // the queue
   int holds;                 // number of elements in the queue
   int first_in;              // pointer to front of the queue
   int last_in;               // pointer to last element in the queue;

public:
   stack();                   // Constructor inits all private variables
   void push(int v);          // place element in queue
   int pop(void);             // remove element from queue
};

//********************************************************************
// Default constructor
//    inits all private variables
//********************************************************************
stack::stack(void) {
   holds = 0;                 // init number of elements in the queue
   first_in = 0;              //   "  pointer to front of queue
   last_in = 0;               //   "  pointer to end of queue
}

//********************************************************************
// Method: Pop
//    removes elements from the front of the queue
//********************************************************************
int stack::pop(void){
   if (holds == 0){
      cout << "**ERROR - Queue is empty.  Zero returned.\n";
      return(0);
   }
   if ( ++first_in > STACK_SIZE-1 ) {
      first_in = 0;
   }
   --holds;
   return buffer[first_in];
}

//********************************************************************
// Method: Push
//    places an element at the end of the queue
//********************************************************************
void stack::push(int v){
   if (holds == STACK_SIZE){
      cout << "**ERROR - Queue is full.  Element: " << v
           << " rejected.\n";
      return;
   }
   if ( ++last_in > STACK_SIZE-1) {
      last_in = 0;
   }
   ++holds;
   buffer[last_in] = v;
}

//********************************************************************
// Testing class stack
//********************************************************************
int main()
{
   stack s;
   s.push(1);
   s.push(2);
   s.push(3);
   cout << s.pop() << endl;
   cout << s.pop() << endl;
   cout << s.pop() << endl;
   return(0);
}