Graphic by Keith Ohlfs

CS111, Wellesley College, Spring 2000

Problem Set 7

Due: Friday, April 7 by 4:00 p.m.

[CS111 Home Page] [Syllabus] [Lecture Notes] [Assignments] [Programs] [Documentation] [Software Installation] [FAQ] [CS Dept.] [CWIS]

About this Problem Set

The purpose of this problem set is to give you experience with tail recursion, while loops and for loops. There are three problems in this problem set.

How to turn in this Problem Set

Softcopy submission: Save the modified PS7IntListOps.java, StepWorld.java and NestedFrames.java files in their respective folders in the ps7_programs folder. Upload the entire folder (including all the other .java and .class files -- everything) to your ps7 drop folder.

Hardcopy submission: Turn in only one package of hardcopy materials. Staple your files together and submit your hardcopy package by placing it in the box outside of Stanzi's office (E106). Your package should include printouts of

Reminders


Problem 1: List Partitioning (4 points)

The following partition() method takes an integer named pivot and a list of integers named L and partitions the list into two lists:

  1. All the elements in L less than pivot.
  2. All the elements in L greater than or equal to pivot.

The two resulting lists are returned as the components of an ObjectPair, which is an Object that has two Object instance variables. The .java file has been setup so that you do not have to write IntListOps.head(L), etc., but instead can simply write head(L), for each of the IntList functions.

 	public static ObjectPair partition (int pivot, IntList L) {
  	 	return partitionTail(pivot, L, empty(), empty());
	}

	public static ObjectPair partitionTail (int pivot, IntList list, 
                                        IntList lesses, IntList greaters) {
  		if (isEmpty(list)) {
    		return new ObjectPair(lesses, greaters);
  		} else if (head(list) < pivot)
  			return partitionTail(pivot, tail(list), 
                         prepend(head(list), lesses), greaters);
  		} else {
    		return partitionTail(pivot, tail(list), 
                         lesses, prepend(head(list), greaters));
  		}
	}

The constructor method for an ObjectPair is defined as follows:

public ObjectPair(Object ob1, Object ob2)
Create a new ObjectPair that has two object instance variables.  These can be 
referred to as object1 and object2.
Thus, we can create an ObjectPair that contains two integerlists as follows:
	IntList L1 = empty();
	IntList L2 = empty();
	ObjectPair listPair = new ObjectPair(L1, L2);

We can then refer to the two IntLists in that ObjectPair as follows:

	IntList firstList = (IntList)listPair.object1;
	IntList secondList = (IntList)listPair.object2;

Part a. The partitionTail() method is a tail recursive method that specifies an iteration in four state variables named pivot, list, lesses, and greaters. Any iteration can be characterized by how the values of the state variables change over time. Below is a table with four columns, one for each state variable of the iteration described by partitionTail(). Each row represents the values of the parameters to a particular invocation of partitionTail().

Suppose that the list A has the printed representation [7,2,3,5,8,6]. Fill in the following table to show the parameters passed to successive calls to partitionTail() in the computation that begins with the invocation partition(5, A). There are more rows shown here than you need, so you should draw only as many rows as you need to in your table:

pivotlist lessesgreaters
     
     
     
     
     
     
     
     


Parts b - d. For each of the parts b - d of this problem, you need to flesh out the code that is found in the file PS7IntListOps.java, in the PS7IntLists folder. The file that you download from Nike simply references the class file PS7IntListAnswers in the program stubs for these methods. (The class file, but not the source file for the answers is included in the PS7IntLists folder). Before you start programming, run the PS7IntListOpsTest.html file in the AppletViewer. Study the behavior of each method so you know what the desired output is. Then replace the line that invokes the method from the answers class with your own code. You can test your code by running PS7IntListOpsTest.html in the AppletViewer.

Part b. It is possible to express any iteration as a while loop. Flesh out the following code skeleton of a partitionWhile() method that behaves just like the above partition() method except that it uses a while loop rather than tail recursion to express the iteration of partitionTail(). Your partitionWhile() method should not call any auxiliary methods, but it should make use of the type ObjectPair to return two lists.

	public static ObjectPair partitionWhile (int pivot, IntList L) {

	}

Part c. In the above partition() method, elements in the two returned lists are in a relative order opposite to their relative order in the original lists. For instance, partitioning the list [1, 4, 8, 3, 6, 7, 5, 2] about the pivot 6 yields the lists [2, 5, 3, 4, 1] and [7, 6, 8].

Suppose that we want the resulting lists to have the same relative order as in the original list. If we are provided with a list reversal method reverse(), we can easily accomplish this by reversing the two lists before putting them into the result array. That is, we can change the line

	return new ObjectPair(lesses, greaters);
within partitionTail() to be
	return new ObjectPair(reverse(lesses), reverse(greaters));

An alternative to using reverse() to get this behavior is to write partition() as a non-tail recursive method. Flesh out the following skeleton of partitionNotTail() which partitions the elements of a list about the pivot but maintains the relative order of the elements in the resulting lists:

	public static ObjectPair partitionNonTail (int pivot, IntList L) {
  		if (isEmpty(L)) {
    		return new ObjectPair(empty(), empty());
  		} else {
    		ObjectPair subresult = partitionNonTail(pivot, tail(L));
        	// flesh out the missing code here ...
  		}
	}

Part d. An important use of the partition() method is a sorting algorithm known as quicksort. Here is the idea behind quicksort:

To sort the elements of a list, partition the elements of the tail of the list around its head into result lists that we'll call lesses and greaters. Then result of sorting the whole list can be obtained by appending the result of sorting lesses to the result of prepending the head of the list to the result of sorting greaters.

For example, if the initial list is [5, 2, 8, 3, 6, 7, 1, 4], then partitioning the tail of the list around the head (5) yields:

	lesses = [4, 1, 3, 2]
	greaters = [7, 6, 8]

By wishful thinking, sorting lesses will yield [1, 2, 3, 4] and sorting greaters will yield [6, 7, 8]. The result of sorting the original list is the result of appending [1, 2, 3, 4] to the result of prepending 5 to [6, 7, 8].

Flesh out a method public static IntList quicksort (IntList L) that uses this idea to sort the elements of a list. You may use IntListOps.append() to append two lists. We have added code so that you may use append() in place of IntListOps.append().


Homework Problem 2: Nested Frames (3 points)

In this problem you will use iteration in to draw two-colored nested frame patterns like those shown below:

Each of the four patterns consists of concentric rectangular frames that have the same thickness and alternate between two colors. In the above example, each target has 10 nested frames, each of whose thicknesses is 1/20 of the dimensions allocated to the pattern. (When the thickness is 1/(2*numberNestedFrames), the frames will evenly fill the alloted Picture space)

In Picture World, such patterns can be drawn as a sequence of concentric filled rectangles, where the rectangles are draw from the outside in. You have been provided with the following method for creating a centered rectangular picture:

public Picture centeredRect (double fraction, Color c)
Returns a rectangle filled with color c that is centered in the picture canvas in which it is drawn. The width and height of the rectangle are each the given fraction of the enclosing picture canvas's width and height. The fraction argument must be between 0 and 1.

For example, using this method, here is a recursive implementation of the nested pattern:

public Picture nestedRec(int levels, double thickness, Color c1, Color c2) {
  if (levels == 0) {
    return empty();
  } else {
    return overlay(nestedRec(levels - 1, thickness, c2, c1),
                   centeredRect(levels*thickness, c1));
  }
}

Here, levels is the number of nested frames, thickness is the defined as 1/levels, and represents twice the thickness of each frame edge, c1 is the outermost color, and c2 is the color that alternates with c1. (Note that thickness = 1/levels is automatically defined in the code you are given). The recursion accumulates a final picture that consists of levels centered rectangles overlayed on top of one another. Note how each level of the recursion decrements levels by 1 and swaps c1 and c2 (so that c2 is the outermost color in the nested subpicture). This strategy is not a tail recursive one, since there is still a pending overlay operation to be performed after the recursion returns.

In the rest of this problem, you will expore three iterative strategies for expressing the nested box pattern. You will start by creating an iteration table for the nested boxes.

Part a. Create an iteration table for creating the NestedFrames picture with iteration. The table should have a column for each of the state variables in the iteration. This will include the state variables: n, thickness, c1, c2, and a Picture variable that contains the answer for the current iteration state. You may also want to add an extra column that contains the picture that will be used in combination with the current answer to generate the next answer in the next row of the iteration table. This extra column should have pictures that can be created from the other state variables and the Picture methods that are available to you (including centeredRect()). Draw out the iteration table for the following invocation:

nestedIter(4,0.25,Color.blue,Color.green);
Turn this in with the hardcopy of your assignment.

Part b. You will be fleshing out the following skeletons in the file NestedFrames.java within the folder NestedFrames:

public Picture nestedIter(int n, double thickness, Color c1, Color c2) {
  // This method contains the initial call to nestedTail, which does all the work.
  return nestedTail(n, thickness, c1, c2, empty());
}
	
public Picture nestedTail(int n, double thickness, 
                          Color c1, Color c2, Picture ans) {
  // nestedTail should be a tail recursive method that returns a Picture 
  // that is the nested frames pattern. 

  // Replace the following stub by a correct definition.
  return empty();
}
	
public Picture nestedWhile(int n, double thickness, Color c1, Color c2) {
  // Use a while loop to accumulate and return a Picture 
  // that is the nested frames pattern.

  // Replace the following stub by a correct definition.
  return empty();
}
	
public Picture nestedFor(int n, double thickness, Color c1, Color c2) {
  // Use a for loop to accumulate and return a Picture 
  // that is the nested frames pattern.

  // Replace the following stub by a correct definition.
  return empty();
}

Your task is to flesh out all three methods so that they have the same behavior as nestedRec. You can test your methods by executing the NestedFrames.html applet. This will give you a window like that pictured at the beginning of this problem. By selecting an integer in the rightmost choice box, you can change the nesting level of the patterns. Your code does not have to take care of calculating the thickness; this calculation is already performed by the testing enviroment.

The patterns are arranged as follows inside the NestedFrames Applet window:

In your definitions, pay attention to the following notes:


Homework Problem 3: Turtle Steps (3 points)

In this problem, you will use a turtle to draw sequences of descending steps made out of square blocks. For example:

The steps are controlled by two parameters:

Below, you are asked to implement two different strategies for drawing such steps. The skeleton code that you need to flesh out can be found in the StepMaker class within the StepWorld.java file within the folder StepWorld. StepMaker is a subclass of Turtle; all lines should be drawn by the fd and bd methods of this subclass of turtle. When you have finished the two parts below, you should save the modified StepWorld.java file in the StepWorld Folder and upload it to your ps7 drop folder.

Part a: Row Decomposition

One way to decompose a set of steps is into a base row of blocks and a smaller set of steps. For instance, the sample steps from above can be decomposed into a row of five blocks and a set of steps with a base of four blocks:

In this part, you are to implement this strategy via the following two methods:

public void stepsRec (int n, int blockSize)
Use recursion to draw a set of descending steps with a base of n blocks, each of which 
is a square of side blockSize, and return the turtle to its initial position and 
heading. The problem should be decomposed into drawing the base row 
of blocks and drawing a smaller set of descending steps above it. 
	
public void row (int n, int blockSize)
Use recursion to draw a row consisting of n blocks, each of which is a square of side blockSize,
and return the turtle to its initial position and heading.

You may wish to define additional auxiliary methods in addition to the two methods described above.

You can test out your code for this part by (1) selecting stepRec in the parameter window; (2) choosing values for n and blockSize in the parameter window; and (3) selecting Run in the Turtle window. The turtle originally starts at a coordinate of (0,0) (in the center of the screen) with a heading of 0 degrees (facing east). In order to test that stepsRec properly returns the turtle to its initial position and heading, the testing code has the turtle turn left 225 degrees and move forward 100 steps after the call to stepsRec.

For example, the following parameter settings

should result in the following picture:

Part b. Rectangle Decomposition

An alternate strategy for drawing the steps is based on the following observation: a set of steps with a base of n blocks can be drawn as the superposition of n rectangles whose widths increase from 1*blockSize to n*blockSize and whose heights decrease from n*blockSize to 1*blockSize. For example, the 5-block base steps shown below result from superimposing the five rectangles to its right so that their lower left points coincide:

Use this idea to implement the following method:

public void stepsFor (int n, int blockSize)
Draw a set of descending steps with a base of n blocks, each of which is a square of side blockSize,
and return the turtle to its initial position and heading. The problem should be decomposed into drawing
n superimposed rectangles as described above. As suggested by  the name stepsFor, a for loop 
should be used to draw the rectangles.  Keep in mind that you can accomplish 
this with only one for loop that uses only one index variable.

You may define auxiliary methods for this problem if you wish.

You can test out your code for this part as in part a, except you should select stepsFor in the parameter window. To ensure that the turtle is returned to its initial position and heading, the testing code will draw the additional line extending from the lower left corner as mentioned in the testing notes for part a.