Assuming you took CS5, at this point in CS60 you know (at least) four programming languages: Python, HMMM, Scheme, and Java (or at least a little Java). However, although all of these languages can do some useful and powerful stuff, in some ways they fall short. Namely, they cannot help you solve "unit arithmetic" problems of the type that you might encounter in your physics class, for example.
So... over the next two weeks, you are going to be implementing your own "unit arithmetic" language. This language will be surprisingly powerful, letting its user store values in variables, and then carry out computation using these values. For example:
def mile 5280 foot
{ 5280.0, [foot], [] }
def x 1 mile
{ 1.0, [mile], [] }
def yard 3 foot
{ 3.0, [foot], [] }
def y 5 yard
{ 5.0, [yard], [] }
y + x
{ 5295.0, [foot], [] }
#y
{ 15.0, [foot], [] }
Notice that the second to last operation has to first convert y and x into their basic units (foot) in order to add them. Similarly, "#" is the "normalizing operator" that converts an expression into its simplest form.
In this week's homework we will build some basic building blocks for this new language. In part 1 (the individual part), you will build a data structure in Java that mimic's scheme's List data structure, called OpenList. The OpenList "shows off" Java's data-structuring capabilities without sacrificing the generality of Scheme. In part 2 (the pair option part), you will build the unit conversion capabilities in scheme. Although our final language will be implemented in Java, you'll work first in scheme and then port your scheme code to Java next week.
You will submit 3 files this week.
There are many, many environments in which you might write Java code. Because CS 60 seeks to get at the fundamentals of computation (and not focus on a particular toolset like Eclipse, JCreator, Visual Studio, NetBeans, etc.) our default setup will use Java from the Dr Java environment.
However, if you have a different favorite environment, you're welcome to use it, but please be sure that you are submitting only your plain-text .java source files so that we will be able to compile and run them!
Setting up Java on the Mac Type javac -version at the command line in the Terminal application; it should already be built-in. The lab Macs are using version 1.5.0_13, though pretty much any version will be fine. We will be concentrating on the core of the language, not the small differences among versions 1.4, 1.5, and 1.6, for example.
Setting up Java on the PC (Windows) You might have java, but might _not_ have java development kit (JDK) which includes the compiler. You will need the compiler to run your code! To find out if you have it, look for the folder C:\Program Files\Java\jdk1.5.0_11\ - or some other version, perhaps jdk1.6.0_07 - the key is that the jdk letters are at the start of the folder name. The jre folder, which stands for java runtime environment can run software, but can not compile it. Thus, you'll need the JDK. If you don't have the JDK and are on a Windows machine, download it from Sun's Java standard edition downloads page.
For both Macs and Windows, you will also need to download JUnit from the following link: http://sourceforge.net/projects/junit/files/junit/. Choose version 4.7. The file you want is junit-4.7.jar. After you have downloaded this file, you need to set your CLASSPATH variable to include this file.
On a PC (Windows) From the Start menu, right click on "Computer" (or "My Computer") and select Properties. Choose "Advanced" or "Advanced System Settings". Then click the "Environment Variables" button. You might have a variable called CLASSPATH. If you do, select it and Edit this variable. At the end of its current value, add a semi-colon, and then the full path to the .jar file you just downloaded. For example, ";C:\Program Files\Java\junit-4.7.jar" (modify this path depending on where you saved the .jar file, but be sure to include the name of the file in the classpath.). If you do not have a CLASSPATH variable already, select New... to create one, and give it the value which is the path to the .jar file you downloaded.
On a Mac This is a little trickier. You have several options. First, you can add the line:
export CLASSPATH = ".:PATH_TO_junit.jar_FILE"
Note that PATH_TO_junit.jar_FILE should be replaced with the full path to the file you just downloaded INCLUDING the file name.
Option 2 applies if you are running from Dr. Java. In Dr. Java, go to Edit->Preferences. Next to Extra Classpath click Add. Then navigate to the junit.jar file you just downloaded, select it, and click OK.
Finally, a third option involves simply setting the classpath as you call Java. When you
70 points
This problem is meant to introduce the implementation of data structures in Java. In addition, it is a chance to either introduce yourself (or review) what Java looks like and how java code is structured. If you haven't used Java before, you may need to review a bit about the language. In particular, there is a starting point for your OpenList.java and OpenListTester.java files on the top-level assignments page. Also, there are on-line references and tutorials available from the references page.
Write a java class named OpenList that implements an (open) linked list data structure. Since the goal of this problem is to transition from Scheme to Java, you will be implementing lists in the same way Scheme does. In fact, this problem is, essentially, the first step in writing the Scheme programming language in Java. Open lists are not modified in the computer's memory (so that there is no worry about side effects) -- instead, each function returns a newly created list (or whatever data is appropriate).
Write your class in a file called OpenList.java. You should start with the OpenList.java file located at the top-level assignments page.
The main method of this OpenList.java contains a couple of tests. You might want to try out the file to begin with and read over the code in order to get a sense of what Java is doing (and how it's organized).
Testing your code! Just as with your Scheme programming, you should test each of your methods as you write it. To do this, we will be using the JUnit test framework. Write your tests in the provided OpenListTester.java file. It's much easier to make small syntactic errors in Java than in Scheme (all those semicolons!) Be sure to try out base cases, as well as recursive ones! You should include one method in this class for each test you write.
Reminder Again, the command
% javac OpenList.java OpenListTester.javawill create a files named OpenList.class and OpenListTester.class in your directory. After that, running
or
% javac *.java
On knuth you will need to run:
%javac -cp ".:/usr/share/junit-4/lib/junit.jar"OpenList.java OpenListTester.java
or
%javac -cp ".:/usr/share/junit-4/lib/junit.jar"*.java
[If you are on a mac and chose CLASSPATH option 3 above, you will also need to use the
-cp option as above, but giving it the correct path to your junit.jar file. Be sure
to include the .: at the beginning!]
%will run all of the tests in the OpenListTester class.java org.junit.runner.JUnitCore OpenListTester
or on knuth:
%java -cp ".:/usr/share/junit-4/lib/junit.jar"org.junit.runner.JUnitCore OpenListTester
or on a mac:
% java -cp ".:/PATH_TO_junit.jar"org.junit.runner.JUnitCore OpenListTester
where PATH_TO_junit.jar is the complete path to the junit.jar file you downloaded above.
In Dr. Java, all of this is much simpler. Make sure you have both files open, then click "Compile" and then "Test" to run your tests.
First, in your OpenList class, you will want to indicate the data that constitute a list. There are two nonstatic data members (first and rest) and one static data member (emptyList) in an OpenList:
private Object first;These are the only data members you will need in your list implementation. You may use others, if you would like -- in fact, there is considerable leeway in how you want to implement OpenList. However, it has to have all of the methods indicated below, and they need to behave as specified!
private OpenList rest;
public static OpenList emptyList = new OpenList(null, null);
Notice that because the data member named first is of type Object, this OpenList implementation will be able to create lists of any Java Objects. Everything in Java is an Object except the types int, double, float, boolean, byte, char, and long. We will use only Strings and OpenLists for this assignment. The next assignment, on the other hand, will put your OpenList class to work... .
In Java and other object-oriented languages, functions that are members of a class are often called methods.
So, in your OpenList class, implement
the following methods:
This toString method is a nonstatic method that returns a String representation of the invoking list. A static version isn't needed. The toString method is used internally by Java whenever your code adds a String to an OpenList, e.g., in code like this:
OpenList L = OpenList.list("Hello","World");
System.out.println("List L is " + L);
This code should output
List L is ["Hello", "World"]You will also use this method quite a bit when writing your unit tests, so you can assume it's working correctly.
String m1 = "mudd";Basically, if compareTo is less than zero, then this is before the input argument in the dictionary.
String m2 = "mit";
if ( m2.compareTo(m1) < 0 ) ... // this test will evaluate to true
String firstOfL = (String)L.first(); // we cast the Object returned by first()Thus, you'll want to use lines like these two in order to grab the two strings you'll need to implement the merge function.
String firstOfM = (String)M.first(); // into the String we know it to be...
Object s = new String("I'm a string.");
if ( s instanceof String ) ... // this test will evaluate to true
if ( s instanceof OpenList ) ... // this test will evaluate to false
No static version is required. Remarkably, Java's compiler checks if an
instanceof expression can possibly
be true - the code won't compile if it can't! In this case, s
is of type Object, which could be
a String or a OpenList.
(The compiler doesn't actually read the code, it
just checks the types....) This problem asks you to write a Scheme function, convert, which implements a "unit-calculator" application. A complete application will be able to convert any unit quantity to any compatible quantity by using an association list of known conversions. Incompatible conversions will result in a set of extra units "left over."
For example, the following call is asking "how would you convert from 1
mph (mile per
hour) to 1 meter per second?" The third input argument to convert,
named db
is a "database" of unit conversions. This "database" is simply an
association list.
> (convert '(1 ("mile") ("hour")) '(1 ("meter") ("second")) db)
(0.447032 () ())
This result is indicating that 1 mile-per-hour == 0.447032 * 1
meter-per-second.
> (convert '(1 ("mile") ("hour")) '(1 ("foot") ()) db)
(1.466666 () ("second"))
This time the result is showing that 1 mile-per-hour == 1.46666
feet-per-second.
Here are the key representations used in Unicalc:
(1.0 ("mile") ("hour")) represents 1 mile per hour
(2.5 ("meter") ("second" "second")) represents 1 meter per second squared
(60.0 ( ) ("second")) represents 60 hertz
(3.14 ( ) ( )) represents about pi radians
To summarize, a Quantity list is a list of three elements: the first is
the quantity (a floating point number), the second is a list of units
in the numerator, and the third is the list of units in the
denominator. Empty lists are required when no units are present. Note
You may assume that the lists of units within the numerator and within
the denominator, if any, are initially in
alphabetical order. Your code needs to make sure
this property is maintained! (define small-dbEach line in the database is thus a list of two elements -- the first element is a string that names some unit and the second element is a Quantity List that is equivalent to that named unit.
'(
("foot" (12.0 ("inch") ()))
("inch" (2.54 ("cm") ()))
("hertz" (1.0 () ("second")))
("mile" (5280.0 ("foot") ()))
("coulomb" (1.0 ("ampere" "second") ()))
("cm" (0.01 ("meter") ()))
("hour" (60.0 ("minute") ()))
("minute" (60.0 ("second") ()))
)
)
Not every unit is defined in terms of others in the database. Some are intentionally left undefined. We'll call the latter basic units. In the above example, ampere, meter, and second are basic units.
A database does not need to contain conversions for everything to everything; that would tend to make it very large and maintenance would be error-prone. Instead, all conversions done by Unicalc are derived from basic ones in the database.
Individual functions You are welcome to decompose this unit-calculator into smaller functions on your own. If you do so, we will simply test your convert function. However, you are also welcome to write the functions described on this page, which will lead you to a full implementation of the unit-calculator. If you use this decomposition, we will grade each function separately.