![]() Graphic by Keith Ohlfs |
Problem Set 8 Due on Tuesday November 13, 2001 at 11pm |
[CS111 Home Page] [Syllabus] [Assignments] [Documentation] [FAQ] [CS Dept.] [CWIS]
ps8_programs
to your drop folder on the cs111 server. Before submitting your work, make sure
that all the files have been saved and the projects compile and run as they should. After
submitting your work, make sure to doublecheck that all the files have been uploaded correctly.
If you need directions on how to submit your work or how to check whether the submission was
successful, please 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).
public static IntList merge(IntList L1, IntList L2) { return mergeTail(L1, L2, empty()); } public static IntList mergeTail(IntList L1, IntList L2, IntList build) { if (isEmpty(L1)) { return append(build, L2); } else if (isEmpty(L2)) { return append(build, L1); } else if (head(L1) <= head(L2)) { return mergeTail( tail(L1), L2, postpend(build, head(L1)) ); } else { return mergeTail( L1, tail(L2), postpend(build, head(L2)) ); } }The mergeTail() method is a tail recursive method that specifies an iteration in three state variables named L1, L2, and build. Any iteration can be characterized by how the values of the state variables change over time. To understand the method better, fill in the iteration table below. The table has three columns, one for each state variable of the iteration described by mergeTail(). Each row represents the values of the parameters to a particular invocation of mergeTail().
Suppose that the list A has the printed representation [1, 3, 5] and
the list B corresponds to [2, 6, 8, 10]. Fill in the following table to
show the parameters passed to successive calls to mergeTail() in the computation
that begins with the invocation merge(A, B). There are more rows shown
here than you need, so you should draw only as many rows as you need to
in your table:
L1 | L2 | build |
[1, 3, 5] | [ 2, 6, 8, 10] | [ ] |
.java
)
file for the answers is included
in the ps8IntLists folder). Before you start programming, run the the project
ps8IntList.mcp
.
Study the behavior of each method so that you know
what the desired output is. Then replace the line that invokes the method
from the answers class with your own code.
Part a. It is possible to express any iteration as a while loop. Flesh out the following code skeleton of a mergeWhile() method that behaves just like the above merge() method except that it uses a while loop rather than tail recursion to express the iteration of mergeTail(). Your mergeWhile() method should not call any auxiliary methods of your own, only the necessary IntList methods such as prepend(), postpend(), and append().
public static IntList mergeWhile (IntList L1, IntList L2) { }Part b. It is possible to express any iteration as a for loop. Flesh out the following code skeleton of a mergeFor() method that behaves just like merge() except that it uses a for loop rather than tail recursion to express the iteration of mergeTail(). Your mergeFor() method should not call any auxiliary methods of your own, only the necessary IntList methods such as prepend(), postpend(), and append().
public static IntList mergeFor (IntList L1, IntList L2) { }Part c. An important use of the merge() method is in a sorting algorithm known as mergesort. Here is the idea behind mergesort:
To sort the elements of a list, split the list roughly in half into two lists that we call lowHalf and highHalf. Recursively sort each of the two halves, and then use merge() to combine the sorted half into a single sorted list.
For example, if the initial list is [5, 2, 8, 3, 6, 7, 1, 4, -8], then splitting yields:
lowHalf = [5, 2, 8, 3] highHalf = [6, 7, 1, 4, -8]If the length of the original list is not even, then lowHalf will be shorter by one than highHalf, as shown here. By wishful thinking (recursion!), sorting lowHalf will yield [2, 3, 5, 8] and sorting highHalf will yield [-8, 1, 4, 6, 7]. The result of sorting the original list is the result of merging the halves [2, 3, 5, 8] and [-8, 1, 4, 6, 7]. There are two base cases in this recursion, a list with zero elements and a list with one element. It is tempting, but unnecessary, to treat a list with length 2 as a base case.
Flesh out a method public static IntList mergeSort (IntList L) that uses this idea to sort the elements of a list. You may use standard IntList methods, including reverse(), as well as the method listPiece() defined in IntListOps.java. The invocation listPiece(L, lo, hi) returns an IntList list containing the elements of the list L that appear in positions lo through hi (inclusive) of L. It is assumed here that the positions of the elements of L are numbered 1 through n, where n is the length of L.
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)For example, using this method, here is a recursive implementation of the nested pattern:
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.
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.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)); } }
Before you start, it will be helpful to draw 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);You don't need to submit the iteration table.
Part a. You will be fleshing out the following skeletons in the file NestedFrames.java within the folder NestedFrames:
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.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(); }
The patterns are arranged as follows inside the NestedFrames Applet window:
The steps are controlled by two parameters:
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:
You may wish to define additional auxiliary methods in addition to the two methods described above.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 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:
You may define auxiliary methods for this problem if you wish.public void stepsWhile (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 stepsWhile, a while loop should be used to draw the rectangles.
You can test out your code for this part as in part a, except you should select stepsWhile 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.