Graphic by Keith Ohlfs
CS111, Wellesley College, Spring 2002

Problem Set 8

Due Tuesday, April 23, at 11:59pm

[CS111 Home Page] [Syllabus] [Assignments] [Documentation] [FAQ] [CS Dept.] [CWIS]

About this Problem Set

The purpose of this problem set is to give you experience with iteration (tail recursion, while loops, and for loops) and arrays. In Task 1 you will use iteration to create pictures of nested boxes. In Task 2, you will use iteration to manipulate lists. In Task 3, you will use iteration to perform "perfect shuffles" of an array.

All code for this assignment is available in the ps8_programs folder in the cs111 download directory on nike.

How to turn in this Problem Set

You are required to turn in both a hardcopy and a softcopy. For general guidelines on problem set submission, including how to submit a softcopy and how to check if you softcopy submission was successful, click here. Please make sure to keep a copy of your work, either on a zip disk, or in your private directory (or, to play it safe, both).

Hardcopy Submission

Your hardcopy packet should consist of:
  1. The cover page;
  2. Your iteration table Task 1a;
  3. Your modified NestedFrames.java file from Task 1b;
  4. Your iteration table Task 2a;
  5. Your modified Concat.java file from Tasks 2b, 2c, and 2d.
  6. Your modified Shuffle.java file from Task 3.
Staple these together, and slide the packet under the door of Elena's office (E127, in minifocus).

Softcopy Submission

You should submit your final version of your ps8_programs folder. In particular,

Task 1: Nested Frames

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 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.

Task 1a: Iteration Table

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.

Task 1b: Iterative Methods

You will be fleshing out the following skeletons in the file NestedFrames.java within the folder NestedFrames in the >ps8_programs folder:
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 yield the same picture as nestedRec, but do so via the iterative process exemplified in your iteration table from Part a. 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:


Task 2: Concat

Background

In Java, recall that if s1 and s2 are strings, then s1 + s2 returns the concatenation of the two strings --- that is, the string which consists of the characters of s1 followed by those of s2. The empty string "" is the identity element for the concatenation operator. For example:

"abc" + "de" evaluates to "abcde"
"abc" + "" evaluates to "abc"
"" + "abc" evaluates to "abc"

Define a prefix of a list L to be a list containing the first n elements of L, where n ranges between 0 and the length of L. We say that the prefix of a list L is proper if it is not equal to L. For example, the proper prefixes of [a,b,c,d] are [a,b,c], [a,b], [a], and []. Note that [a,b,c,d] is not a proper prefix of itself.

Given a list of strings L, we define the concat of L to be another list of strings where each string in the resulting list is formed by concatenating the elements of a proper prefix of L with one of the elements not in that proper prefix. For example, for the list L = [a,b,c,d] we have:

So the concat of L is the list [abcd, abc, abd, ab, ac, ad, a, b, c, d]. The elements of the resulting list should be in the order implied by the example. That is, the elements formed from longer prefixes should go before elements formed from shorter ones; and, for a particular prefix, the elements should have the relative order of the associated non-prefix elements from the original list.

The following concatIter() method correctly calculates the concat of its argument list.

// Assume that L is a list of strings
public static StringList concatIter (StringList L) {
return concatTail(L, "", reverse(L), empty());
}


public static StringList concatTail (StringList input,
String s,
StringList rev,
StringList output) {
if (isEmpty(input) && isEmpty(rev)) {
return output;
} else if (isEmpty(rev)) {
return concatTail(tail(input),
s + head(input),
reverse(tail(input)),
output);
} else {
return concatTail(input,
s,
tail(rev),
prepend(s + head(rev), output));
}
}

The concatIter() method uses a black-box reverse() method that returns a list containing the elements of its input list in reverse order. For instance invoking reverse on the list [a,b,c,d] yields the list [d,c,b,a]. For this problem, it does not matter how reverse() is implemented.

Here is the result of invoking concatIter() on some sample input lists:

concatIter([ ]) = [ ]
concatIter([e]) = [e]
concatIter([d, e]) = [de, d, e]
concatIter([c, d, e]) = [cde, cd, ce, c, d, e]
concatIter([b, c, d, e]) = [bcde, bcd, bce, bc, bd, be, b, c, d, e]
concatIter([a, b, c, d, e]) = [abcde, abcd, abce, abc, abd, abe, 
                               ab, ac, ad, ae, a, b, c, d, e] 

The above code for concatIter() can be found in the file Concat.java within the Concat folder of the ps8_programs folder. The Concat.java file also contains the skeletons of alternative methods for the concat function that you will define in parts 2b, 2c, and 2d below. The Concat.java file has been configured in such a way that it is possible to directly refer to the standard StringList functions (e.g., prepend(), head(), tail(), append(), reverse(), etc.) as shown in the above method definitions.

The Concat folder also contains a main() method that tests the concatIter method from above as well as the alternative versions of concat that you will define in parts 2b, 2c, and 2d below.

Problem 2a: Iteration Table

The concatIter() method calls a tail recursive method concatTail() that implements an iteration. Draw an iteration table that shows the values of the four state variables input, s, rev, and output for all invocations of concatTail() when concatIter() is called on the list [a,b,c]. Your iteration table should be a table with four columns labeled input, s, rev, and output. You should treat reverse() as a black-box method; that is, you need not show any details of how reverse() works.

Problem 2b: While Loop

The iteration expressed by the concatIter() and concatTail() methods can also be expressed using a while loop. Demonstrate this by fleshing out the skeleton of the concatWhile() method in the file Concat.java. This method should perform the same iteration as concatIter() and concatTail() except that it should a while loop to express the iteration rather than tail recursion.

Problem 2c: For Loops

The iteration expressed by the concatIter() and concatTail() methods can also be expressed using a pair of nested for loops. Demonstrate this by fleshing out the following skeleton of the concatFor() method in the file Concat.java:

public static StringList concatFor (StringList L) {
  statements1
for (StringList input = expression1; expression2; statement1) {
statements2
for (StringList rev = expression3; expression4; statement2) {
statements3
}
statements4
}
statements5
}

You should fill in each part of the skeleton labeled expression with an appropriate expression, each part labeled statement with a single statement, and each part labeled statements with zero or more statements. Your concatFor() method should perform the same iteration as concatIter() and concatTail(), except the iteration is expressed via for loops rather than tail recursion.

Problem 2d: List Recursion

The concat function can also be expressed via a non-tail recursive method. Demonstrate this by fleshing out the details of the concatRec() skeleton in the file Concat.java. The general case for the recursion when invoking concatRec(L) should be expressed in terms of concatRec(tail(L)) and the following auxiliary method concatAppend() (which has been defined for you in Concat.java).

// The following is an auxiliary method to be used for defining concatRec().
// Assume that L1 and L2 are lists of Strings.
// Return a list that is the result of appending two lists:
// (1) the result concatenating s in front of every element of L1
// (2) L2.
public static StringList concatAppend(String s, StringList L1, StringList L2) {
if (isEmpty(L1)) {
return L2;
} else {
return prepend(s + head(L1), concatAppend(s, tail(L1), L2));
}
}

A few notes:


Task 3: Perfect Shuffles

Let A be an array of integers with an even number L of elements. A perfect shuffle of A is an operation that permutes the elements of the array by interleaving the elements of the segment A[0..(L/2)-1] with the elements of the segment A[(L/2)..L-1]. For concreteness, consider a length 8 identity array. (An identity array is an array in which the slot with index i holds the integer i.):

[0, 1, 2, 3, 4, 5, 6, 7]

The result of a perfect shuffle on the above array is:

[0, 4, 1, 5, 2, 6, 3, 7]

A second perfect shuffle gives:

[0, 2, 4, 6, 1, 3, 5, 7]

In the case of L=8 a third perfect shuffle results in the original array:

[0, 1, 2, 3, 4, 5, 6, 7]

Let us call the number of perfect shuffles it takes to return an array to its original state the shuffle period of the array. So the shuffle period for L=8 is 3. Your goal in the problem is to write a method printShufflePeriods that prints each even array length and its associated shuffle period within a given interval of array lengths:

public static void printShufflePeriods (int lo, int hi)
Assume lo and hi are even integers. For each even integer k in the interval [lo, hi], print a line whose contents is shufflePeriod(k)=p, where p is the shuffle period for an array of length k.

As part of your solution, you should implement the following auxiliary methods:

public static int [] identity (int L)
Return an identity array with length L.

public static bool isIdentity (int [] A)
Return true if A is an identity array, and false otherwise.

public static int [] perfectShuffle (int [] A)
Return a new array whose elements are the perfect shuffle of the elements of A. The array A should not be modified by this operation. Assume that A has even length.

public static int shufflePeriod (int L)
Return the shuffle period of an array with length L. Assume that L is even.

You should write all your code in the file Shuffle.java within the Shuffle folder in the ps8_programs folder. This file initially contains a nearly empty declaration of a Shuffle class. You should populate it with implementations of all the methods specified above. To show that your methods work, you should have the main method display the shuffle periods for all even array lengths in the range [2..100]. E.g.

    The shuffle period for length 2 is 1.
    The shuffle period for length 4 is 2.
    The shuffle period for length 6 is 4.
    The shuffle period for length 8 is 3.
    ...

Notes: