CS251: Programming Languages

Homework 3

Due Thursday, February 18

This homework covers Chapter 5 of the MacLennan text on Pascal. All of the following problems are required:

(1) MacLennan 5.13 (p. 221) First, describe another way in which the intent of this case statement could be coded in Pascal that would not require enumeration of all the possible digits and letters. Note that if a variable x is of type char in Pascal, comparison operators like =, <>, <, >, <= and >= can be used. For example, the boolean expression (x < 'n') will be true if x is assigned to a character that occurs earlier in the alphabet (assuming that x is also a lower-case letter). When considering an extension to Pascal, consider how the structure of a case statement could be generalized to allow greater flexibility in how the different possible case labels (values on the left side of the case clauses) are specified. Would such an extension compromise any of the basic principles underlying the design of Pascal (i.e. the (1) simple/readable/understandable, (2) safe/reliable or (3) efficiency principles)?

(2) In the old days, it was not possible to read and write the values of an enumerated variable type directly for program input/output, and it became necessary to write special statements such as the following:

case day of
Monday: writeln('Monday');
Tuesday: writeln('Tuesday');
Wednesday: writeln('Wednesday');
Thursday: writeln('Thursday');
Friday: writeln('Friday');
end;

In this case, it would clearly be far more convenient to write:

writeln(day);

When reading user input into an enumerated type variable, code was written to process individual characters of the user's input string to see which enumerated value they entered. This was very awkward, and modern Pascal compilers (such as THINK Pascal) allow direct input into and output from enumerated type variables. Considering the implementation of enumerated types (as described, for example, on page 184 of the text), why do you think early versions of Pascal did not allow the direct input/output to and from enumerated type variables? What information needs to be preserved through the compilation process in order to make this direct input/output possible?

(3) This problem addresses the advantages of allowing procedures and functions to be passed as parameters in Pascal. For the following examples, suppose that maxindex is a constant and intarray is defined as follows:

type intarray = array [1..maxindex] of integer;

Consider the following procedures and function that operate on intarrays:

procedure write_array (var a: intarray);
var i: integer;
begin
for
i := 1 to maxindex do
writeln(a[i]);
end;

procedure square_array (var a: intarray);
var i: integer;
begin
for
i := 1 to maxindex do
a[i] := a[i] * a[i]
end;

function sum_array (var a: intarray): integer;
var sum, i: integer;
begin
sum := 0;
for i := 1 to maxindex do
sum := sum + a[i];
sum_array := sum;
end;

All three are very similar! All three bodies have a portion which has the form:

var i: integer;
begin
for
i := 1 to maxindex do
something that depends on a[i]
end;

This violates Lyn Turbak's Fundamental Rule of Computer Science:

Never write the same idiom more than once!
Instead, capture common patterns as reusable abstractions.

A few programming languages supply mechanisms that help to capture the kinds of patterns seen above in a reusable way. They are all based on the idea of treating functions and procedures like other values in the language (e.g. integers, booleans, strings, records, arrays, ...). Pascal's functional and procedural parameters are a (somewhat limited) example of such a mechanism. Here is how to capture the above idiom in Pascal:

procedure traverse_array (var a: intarray;
procedure action (var a: intarray; i: integer));
var i: integer;
begin
for
i := 1 to maxindex do
action(a, i);
end;

traverse_array takes two arguments: an intarray, and a procedure that itself takes two arguments (an intarray and an integer). The notion of a procedure taking another procedure as an argument may seem strange to you, but similar ideas crop up again and again in mathematics. For example, summations, derivatives, and integrals are all examples of functions that take other functions as arguments.

Here is how traverse_array can be used to implement the first procedure from above:

procedure write_array (var a: intarray);
procedure write_element (var a: intarray; i: integer);
begin
writeln(a[i])
end;
begin
traverse_array(a, write_element);
end;

Rewrite the other procedure and function from above, square_array and sum_array, using the traverse_array procedure. You will see that these alternative definitions are not any shorter than the original ones, and to the untrained eye, they probably seem a lot more complicated. What's the point? What are the advantages of capturing a common programming pattern in a single, generic procedure, and creating flexibility by allowing this procedure to have procedures or functions as parameters? Keep in mind that the generic program pattern could be far more complex than what is embodied in the traverse_array procedure.

(4) A major problem with Pascal is that every array of the same type must have exactly the same length. This makes it difficult to write routines that manipulate arrays of different lengths. A standard technique for ameliorating this problem is to use large arrays of a single standard length to simulate the contents of a smaller array. For example, a five-element array can be represented as the first five elements of a thousand-element array in which the last 995 elements contain "garbage". It is common to pair with each large array a length indicator that specifies the length of a smaller array being simulated by the larger one. This allows a program to determine which part of the large array contains useful data, and which part contains garbage. Design a new variable type using a data structure that is available in Pascal, which uses the above to store variable-length arrays (i.e. this type must somehow contain both an array to store the actual contents of the array, and a length indicator). Given the structure of the new variable type that you defined, suppose you declared a variable VLarray (for "Variable Length array") whose type is this new type that you defined. Construct a for statement that steps through the current contents of the array (as far as its current length) and sums the contents of the array. What are the disadvantages of this approach to simulating variable-length arrays?