Assignment 2: Designing methods for complex data
Goals: Learn to design methods for complex class hierarchies. Practice designing the representation of complex data.
2.1 Instructions
This homework should be done with your lab partner.
the names of classes,
the names and types of the fields within classes,
the names, types and order of the arguments to the constructor,
the names, types and order of arguments to methods, or
filenames,
You will submit this assignment by the deadlines using the course handin server. Follow A Complete Guide to the Handin Server for information on how to use the handin server. You may submit as many times as you wish. Be aware of the fact that close to the deadline the server may slow down to handle many submissions, so try to finish early. There will be a separate submission for each problem - it makes it easier to grade each problem, and to provide you with the feedback for each problem you work on.
Your submissions for this homework will be organized as follows:
Homework 2 Problem 1: Webpages.java
Homework 2 Problem 2: UniqueWebpages.java
Homework 2 Problem 3: MoreLists.java
Homework 2 Problem 3 Examplar: ExamplesLoString.java
Homework 2 Problem 4: Lightning.java
Homework 2 Problem 4 Examplar: ExamplesLightning.java
Due Date: Wednesday, January 22nd, 9:00 pm
Practice Problems
Work out these problems on your own. Save them in an electronic portfolio, so you can show them to your instructor, review them before the exam, use them as a reference when working on the homework assignments.
Problem 10.6 on page 102
Problem 11.2 on page 113
Problem 12.1 on page 125
Problem 14.7 on page 140
Problem 15.2 on page 149
Problem 15.3 on page 149
Problem 15.8 on page 171
Problem 1: Webpages
The following DrRacket data definition describes a website as a collection of Webpages:
;; A Webpage is a (make-webpage String [List-of Content]) (define-struct webpage (name content)) ;; A Content is one of ;; -- Text ;; -- Picture ;; -- Hyperlink ;; A Text is a (make-text String Number Boolean) (define-struct text (name num-lines in-markdown?)) ;; A Picture is a (make-picture String String Number) (define-struct picture (name description megabytes)) ;; A Hyperlink is a (make-link String Webpage) (define-struct link (text destination))
We are giving you the names of the classes or interfaces you will probably need
—
A reminder on naming conventions: For lists of data, the names of the interface should always start with ILo, while the two classes’ names start with MtLo for the empty lists and ConsLo for the nonempty lists; all three of these names should be followed by the name of the datatype of the elements of the list. So we would have ILoString, MtLoString, ConsLoString to represent lists of Strings, ILoBook, MtLoBook, ConsLoBook to represent lists of Books, etc.
Note: if it helps you at all in this assignment, you may assume that every name in this website is distinct.
Draw a class diagram for the classes that represent this data definition. (It is optional to submit your diagram. You can draw this on paper, or in ASCII art as a comment in your submitted file.)
Define Java classes that represent the definitions above.
Describe (in English, or in a diagram, or in code...) a website that has at least one text section, two pictures, three webpages, and four hyperlinks. It should be different from the one below.
Design the data representation of the example you just described.
Name your examples class ExamplesWebpages
In the ExamplesWebpages class design the example of the following:
- A webpage named "Fundies 2 Homepage", with the following content:
A text section named "Course Goals", with 5 lines that are written in Markdown
A text section named "Instructor Contact", with 1 line that is not written in Markdown
A picture "Eclipse", with the description "Eclipse logo" that is 0.13 megabytes
A picture "Coding Background", with the description "digital rain from the Matrix" that is 30.2 megabytes
A hyperlink "Course Syllabus" to Syllabus
A hyperlink "Course Assignments" to Assignments
- A webpage named "Syllabus", with the following content:
A picture "Java", with the description "HD Java logo" that is 4 megabytes
A text section named "Week 1", with 10 lines that are written in Markdown
A hyperlink "First Assignment" to Assignment 1
- A webpage named "Assignments", with the following content:
A text section named "Pair Programming", with 10 lines that are not written in Markdown
A text section named "Expectations", with 15 lines that are not written in Markdown
A hyperlink "First Assignment" to Assignment 1
- A webpage named "Assignment 1", with the following content:
A picture "Submission", with the description "submission screenshot" that is 13.7 megabytes
Name the "Fundies 2 Homepage" example homepage. Our test program will check that the field homepage in the class ExamplesWebpages represents this information. (You may name the other websites, and all the items inside them, anything you like, though the names should be reasonably descriptive.)
Design the method totalCredits that computes the total number of credits it costs to build the website. Hosting a website costs money, and the web hosting provider gives their rate in terms of credits. Lines of text are free. But pictures are not: For the total number of megabytes (rounded up) of all pictures, each megabyte costs 50 credits. (Note the order of operations here: is it the case that the rounded-up total of image sizes is the same as the total of rounded-up image sizes? Make sure you have at least one example whose that demonstrates the expected behavior, and leave a comment explaining why the answer is the way it should be.)
Hint: you will need some helper methods for this...
Tricky! Design the method pictureInfo that produces one String that has in it the title of all pictures reachable from a webpage, with their description in parentheses, and each separated by comma and space.
So for the Fundies 2 Homepage example above this String would be
"Eclipse (Eclipse logo), Coding Background (digital rain from the Matrix), Java (HD Java logo), Submission (submission screenshot), Submission (submission screenshot)"
Note: You can combine two Strings with a + operator, or by invoking the method concat: assuming both s1 and s2 are Strings, then both s1 + s2 and s1.concat(s2) do the same thing as the BSL expression (string-append s1 s2).
Note: There is a comma and space between any two entries, but not after the last one.
Note: Using myString.substring(...) is a cheesy and sloppy way to solve this problem. Find a cleaner solution that doesn’t need it.
In a comment in your file, explain why some methods double-count some information. Also tell us if there are any other methods in your code where this duplication occurs. Hint: look very carefully at the output from the tester library when it shows your data.
Problem 2: Avoiding repetition
When your boss compared the output of totalCredits with the actual bill from the web hosting provider, they found that you overestimated how much the bill was. There were some repeated appearances of features in the methods you implemented. For this problem, copy your Webpages.java file to a new UniqueWebpages.java file, and fix that repetition. Note: the automated tests for the above problem expect that repetition, so you cannot submit the same file for both of these problems.
Hint: you should not need to create any new data definitions (i.e. interfaces) for this problem or revise existing ones (i.e. create new classes), though you may need to define some additional helper methods for existing ones.
Problem 3: Completing list-of-string methods for other representations
This is a much broader conception of the usefulness of interfaces, than simply as “tags” for indicating the options of union data. Most of our uses of interfaces this semester will be for union data, and we’ll explore this flexibility in more detail in OOD; for now, I want to give you a small “sneak preview” of that future mindset.
Hint: As a strong suggestion: build lots of examples of lists for this problem, and consider building multiple examples that represent the same elements in the same order, but that are constructed using different mixes of the classes above.
Design the implementation of a reverse() method for all four implementation classes of ILoString —
MtLoString, ConsLoString, SnocLoString and AppendLoString — whose job is to produce a new list with its elements in reversed order from the original list. Your implementations can take advantage of the fact that all four of these classes now exist... In a comment, explain whether your method implementations follow the same design as reverse did in Fundies 1, and explain why. Design a method normalize(), that takes the current list and produces a list of the same items in the same order, but that uses only ConsLoString and MtLoString, and simplifies away any uses of SnocLoString and AppendLoString. Hint: the cleanest solution for this method uses a helper with an accumulator. Be sure to explain in a comment what your accumulator represents, if you use one.
A left scan of a list is similar to applying foldl to a list, except that it returns a list of all the intermediate results instead of just the final one. (For example, (scanl + 0 '(2 5 1 0)) produces '(2 7 8 8), these being 0+2, 0+2+5, 0+2+5+1 and 0+2+5+1+0 respectively.) Design a method scanConcat on ILoString that left-scans across the list and concatenates all the strings, and produces a list of the results. Again, implement this method for all four classes.
You do not need to implement other list methods (e.g. sort, size, etc.) for this problem, though you may choose to do so to aid your debugging or testing.
Examplar submissions
For this problem, we are going to to supply an additional submission on Handins. You will write examples and test cases against a placeholder implementation of the classes and interface needed for this problem, and we will run your test cases against multiple implementations: both correct and buggy! Your goal is to write sufficient test cases to distinguish all the correct implementations from all the buggy ones. We call the correct implementations wheat, and the buggy ones chaff: your goal is to separate the wheat from the chaff.
Create a separate project for this tests-only assignment.
Download the lists.zip file and unzip it into your src/ directory: it should create a lists subdirectory with five files in it.
- Create a file ExamplesLoString.java in your src directory, with the following initial contents:
import lists.*; import tester.Tester; class ExamplesLoString { // your examples go here } You must not modify the supplied Java files: they’re provided only to ensure that your tests match the types expected by our implementations.
You will submit only the ExamplesLoString.java file; do not submit the support files we gave you.
Make sure you’ve worked through the lab this week to understand what Examplar is showing you here. Focus on improving each section of the Examplar results in order: first ensure Correctness of your tests, then improve Thoroughness, and once you have both of those working, improve Precision and Usefulness. It is possible to get 100% Precise and Useful results...but it may not be worth your time to do so. Designing a Correct and Thorough suite of examples is a necessary skill for designing an implementation for anything, but designing a Precise and Useful test suite is a refinement on top of that.
Once you’ve successfully written your Examplar suite, you should be able to copy it in to your tests for the rest of this problem, and confirm that your own implementation satisfies your own Examplar examples. (You will need to remove the import lists.*; line from the top, but everything else should be unchanged.
Problem 4: Lighting up the night
In this problem, you will be drawing and working with tree-shaped data representing a lightning bolt as it flashes through a storm.
interface ILightningBolt { /* see methods below */ } // represents one final endpoint of a lightning bolt class Tip implements ILightningBolt { } // represents a straight section of a lightning bolt class Segment implements ILightningBolt { // How long this piece of the bolt is is int length; // The electric current carried in this part of the bolt, measured in kilo-Amperes int current; // The angle (in degrees) of this flow, relative to the +x axis double theta; // The rest of the lightning bolt ILightningBolt bolt; } // represents the lightning bolt splitting in two class Fork implements ILightningBolt { // How long the left and right branches are int leftLength; int rightLength; // The electric current in each part of the bolt, measured in kilo-Amperes int leftCurrent; int rightCurrent; // The angle (in degrees) of the two branches, relative to the +x axis, double leftTheta; double rightTheta; // The remaining parts of the lightning bolt ILightningBolt left; ILightningBolt right; }
A Segment at an angle of 90 degrees is running straight up; a Fork with a left angle of 135 degrees and a right angle of 45 degrees points on both upward diagonals. (The tips of the bolt don’t need an angle, since we can just draw a small dot to indicate the lightning stops there.)
Design the method WorldImage draw(), that renders your ILightningBolt to a picture. Draw all forks and segments using blue RectangleImages with width proportional to current. You can draw the tips of the bolt however you wish; the examples below use a small filled circle. You will almost certainly want to work with pinholes in your images: read the quick-start guide below, and the documentation for more information. (As a hint: a pinhole singles out a special point in your image, such that you can combine multiple images and align them at their pinholes. You will want your image-producing functions to position a pinhole in their results so that each recursive call produces an image with a usefully-placed pinhole; and then you’ll want to move the pinhole of the result so that it is again usefully placed for its caller...)
You do not have to write extensive tests for this method, but you should write at least some simple plausibility checks. See below for more information about getting started with the image library. We will not write automated tests for this method, but will grade it manually.
In a natural lightning bolt, the downsegment parts of the bolt (i.e., closer to the tips) should always have at most as much current as their upsegment parts —
since some of the current is wasted as heat and light. Design a method isPhysicallyPossible(), that computes whether this lightning bolt is physically possible: that is, there is never more current flowing out to the tips than are flowing in from the start of the bolt. Design the method ILightningBolt combine(int leftLength, int rightLength, int leftCapacity, int rightCapacity, double leftTheta, double rightTheta, ILightningBolt otherBolt). This method takes the current bolt and a given bolt and produces a Fork using the given arguments, with this bolt on the left and the given bolt on the right... but with a twist, literally.
Here are two bolts, drawn in shades of different colors so we can keep them apart (the colors have no other meaning, and your images do not need to be multi-colored):
BOLT1 = new Fork(30, 30, 10, 10, 135, 40, new Tip(), new Tip())
BOLT2 = new Fork(30, 30, 10, 10, 115, 65, new Tip(), new Tip())
If we attach each of them to segments running at 90 degrees (i.e., straight up), we get:
new Segment(40, 10, 90, BOLT1)
new Segment(50, 10, 90, BOLT2)
Now suppose we want to attach those two segments to form a fork, with the left segment at 150 degrees and the right segment at 30 degrees (for example). We would get
BOLT1.combine(40, 50, 10, 10, 150, 30, BOLT2)
Your method should implement this combination step. Note that this result is different from simply placing both bolts in a fork directly:
new Fork(40, 50, 10, 10, 150, 30, BOLT1, BOLT2)
You can deduce why this is from the interpretations of the various fields in the data definitions above, and design helper methods to implement the desired behavior accordingly.
Design the method double getWidth() that returns the width of the bolt, from the leftmost tip to the rightmost tip. For simplicity, ignore the actual thicknesses of segments or the branches of a fork themselves, and just assume that the thickness of each bolt is zero.
Hint: your method should work for any instance of this data, so be creative in coming up with a large variety of examples. Try sketching them on paper or a whiteboard first, before coding them up.
Reminder: given a length $l$ and an angle $\theta$ in radians, the x-coordinate is $l \cos\theta$, and you can compute cosines using Math.cos. Note: because Math.cos returns a double, but Posns require ints, you’ll need to coerce the returned cosine value to an int, like this: ((int)Math.cos(whatever...)). I personally always use the extra parentheses around the outside; they’re not always necessary, but they’re often clearer to read this way.
Examplar submissions
We are also supplying an additional submission on Handins for Examplar test cases. Follow similar instructions as above, using the lightning.zip file above, and with starter code
import lightning.*; import tester.Tester; import javalib.worldimages.*; class ExamplesLightning { // your examples go here }
You do not need to write Examplar tests for the draw() method, but you should test the other three methods.
Using the javalib library
The javalib library provides the support for the design of interactive games and creating images composed by combining geometric shapes as well as image files. See The Image Library for more information.
To use the library, download the javalib file above and add it to your project the same way you have added the tester library.
At the top of the .java file where the library is used, add the following import statements:
import tester.*; // The tester library import javalib.worldimages.*; // images, like RectangleImage or OverlayImages import javalib.funworld.*; // the abstract World class and the big-bang library import java.awt.Color; // general colors (as triples of red,green,blue values) // and predefined colors (Color.RED, Color.GRAY, etc.)
boolean testImages(Tester t) { return t.checkExpect(new RectangleImage(30, 20, OutlineMode.SOLID, Color.GRAY), new RectangleImage(30, 20, OutlineMode.SOLID, Color.GRAY)); }
boolean testFailure(Tester t) { return t.checkExpect( new ScaleImageXY(new RectangleImage(60, 40, OutlineMode.SOLID, Color.GRAY), 0.5, 0.25), new RectangleImage(30, 15, OutlineMode.SOLID, Color.GRAY)); }
Finally, you can display your images so that you can see whether you’re on the right track, as follows:
boolean testDrawBolt(Tester t) { WorldCanvas c = new WorldCanvas(500, 500); WorldScene s = new WorldScene(500, 500); return c.drawScene(s.placeImageXY(myBolt.draw(), 250, 250)) && c.show(); }
See The Image Library for more information.