![]() Graphic by Keith Ohlfs |
Problem Set 8
|
[CS111 Home Page] [Syllabus] [Assignments] [Documentation] [FAQ] [CS Dept.] [CWIS]
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.
NestedFrames.java
file from Task 1b;
Concat.java
file from
Tasks 2b, 2c, and 2d.
Shuffle.java
file from Task 3.
ps8_programs
folder. In particular,
NestedFrames
subfolder should
contain your final version of NestedFrames.java
.
Concat
subfolder should
contain your final version of Concat.java
.
Shuffle
subfolder should
contain your final version of Shuffle.java
.
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.
nestedIter(4,0.25,Color.blue,Color.green);Turn this in with the hardcopy of your assignment.
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:
nestedRec()
.
nestedIter()
.
nestedWhile()
.
nestedFor()
.
In your definitions, pay attention to the following notes:
while
loop has the syntax
while (test-expression) { body-statements }
and a Java for
loop has the
syntax
for (init-statement; test-expression; update-statement) { body-statements }
A Java for
loop is
equivalent to the following
while
loop:
init-statement; while (test-expression) { body-statements; update-statement; }
levels
by 1. You should not decrement
levels
by 2!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.
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.
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.
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.
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:
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.):
The result of a perfect shuffle on the above array is:
A second perfect shuffle gives:
In the case of L=8 a third perfect shuffle results in the original array:
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:
main
method.
IntArray
that supplies
the following two class methods:
public static String toString (int [] A);
Returns a string representation of the given integer array. For instance, ifx
is an integer array with elements{7,-4,3,2,8}
, thenIntArray.toString(x)
returns the string"{7,-4,3,2,8}"
.
public static int [] fromString (String s);
Returns an integer array specified by the string representations
. For instance,IntArray.fromString("{7,-4,3,2,8}")
returns the 5-element integer array{7,-4,3,2,8}
.