CS 303E: Assignment #1001 (Fall 2004)
Linked Structures; More Inheritance and Packages (200 Points)

Due: Sunday evening, November 21 by midnight via turnin. If you have problems with turnin from outside the Elements Lab, be sure to use turnin from the Elements Lab so that you submit your work on time.

Purpose: This assignment is a continuation of Assignment #1000 which contained exercises demonstrating inheritance, packages, recursively-defined classes, and class collaborations. In particular, this assignment asks you to implement a general-purpose data structure.

Remember: If you aren't clear on a topic covered in this class, you can post on the class newsgroup or you can attend the many office hours held by the teaching staff. In particular, if you're not clear on any of the terms or concepts described in this assignment, please ask questions. The earlier you ask your questions, the better!

Further, remember that Assignment #1000 established a new requirement that your Java class definitions should all be placed into package "edu.utexas.cs303e.student" or, when explicitly stated, a sub-package thereof. If an exercise does not explicitly state the package, you should place it into edu.utexas.cs303e.student. For example, if an exercise asks you to place your implementation into a "class named AnArbitraryName", your source code should define a class which has full name edu.utexas.cs303e.student.AnArbitraryName.

Part 1: Phrase Translation. Assignment #1000 asked you to implement a class named Phrase that represented the words of a phrase by defining a collaboration with another class named Word. You also implemented a subclass of Word named CognatePair. If you don't recall the details of these classes, you should first review Assignment #1000. This assignment asks you to extend Phrase to implement a simple language translator from English to Pig Latin. The basic idea can be further extended, with some additional work (not in the scope of this assignment!), to implement simple-minded language translation as per babelfish. Sophisticated language translation, though, is much harder.

  1. (60 points) Implement a class named CognatePhrases (in which package?) by extending your previously defined class Phrase. This new class will also represent a phrase, as did Phrase, except that the phrase will be represented in two languages. For our purposes, we'll think of one language as being English and we'll call the other language the cognate language. Implement the following constructors and methods:

    At this point, your implementation of CognatePhrases should be complete, but you should test it to ensure that it works properly. The following code, if placed into a standard main method in CognatePhrases, is a test case that uses your FrenchCognate class (which must be imported so that this main method will compile):

    CognatePair one = new FrenchCognate ("one", "un") ;
    CognatePair young = new FrenchCognate ("young", "jeune") ;
    CognatePair heart = new FrenchCognate ("heart", "coeur") ;
    CognatePhrases phrase = new CognatePhrases (heart) ;
    phrase = new CognatePhrases (young, phrase) ;
    phrase = new CognatePhrases (one, phrase) ;
    System.out.println (phrase) ;
    System.out.println (phrase.toPhraseString()) ;
    System.out.println (phrase.toCognateString()) ;
    
    The above code, when executed, should print the following:
    Cognates in French: ("one","un") ("young","jeune") ("heart","coeur")
    one young heart
    un jeune coeur
    
    When you've completed your implementation, put your Java source code into a file named CognatePhrases.java and submit it via turnin. You may include a standard main method if you'd like, but you won't be graded on that part of your implementation.

  2. (40 points) Implement a class named PigLatin, in package edu.utexas.cs303e.student.pig_latin, that extends the class CognatePhrases (remember to import it) to translate English phrases to corresponding phrases in Pig Latin. You should implement two constructors, a public static factory method, and a standard main method as described below (and you may implement as many private helper methods as you want):

    Put your PigLatin class into a file named PigLatin.java and submit it via turnin.

Part 2: List Data Structures. Have you ever tried to get yourself organized? You probably started by making a list. Maybe it was a list of assignments, or a list of classes to take, or maybe even list of relatives to whom you owe money. If you've ever had to buy a lot of items (clothing, school supplies, books, ...), you probably made a shopping list. If you've ever organized a formal gathering, such as a wedding or a political rally or a motorcycle race, you probably made a list of people to invite.

The point is that a list is a fundamental way of organizing information that is largely independent of the type of information. Similarly, in computer programming, lists are a fundamental building block for representing data. In this part, the core task is to implement a recursively-defined list that is generic (which means that it can be used with any type of data).

  1. (25 points) The starting point is to define a class to hold a list item. Conventionally, the most common name for a class like this seems to be Node, so that's the name we'll use. Put it into package edu.utexas.cs303e.student.utility. Your version will be immutable, but it's also possible to implement a mutable version. When you code the constructors and method described below, look for similarities to the class Phrase that you implemented for Assignment #1000. Also, look for differences! The Node class should have package access, as should its constructors, and it must have the following constructors and methods:

    Put your Node class into a file named Node.java and submit it via turnin.

    Did you answer the question about Node's add(Object) method above? How can add(Object) be used to quickly create multi-item lists? If you haven't determined the answer yet, it's time to ask someone! This is a fundamental operation. For example, suppose someone wants to use your Node class to create a sequence of three Node objects that, in turn, reference three different items. The first Node in the sequence is to be referenced by a variable (of type Node) named head. A diagram of the desired result is shown below:

    This could be achieved with the following code that uses constructors:
    Node head = new Node ("xyz") ;
    head = new Node (new Integer (17), head) ;
    head = new Node (Double.parseDouble ("61.7"), head) ;
    Do the same thing with one line of code using the add method.

Ok, if you answered the question at the end of the previous exercise, you now should realize that the purpose of Node is to use it as a building block to represent lists (aka, sequences) of other objects. When you consider lists, one question to ask is, "How can I compute with the items in a list?" The two answers are: Recursion, as we did with the Phrase class; and Iteration (aka, loops) as we've done for sequences of actions. If we want to write a loop to process a sequence of items in Node objects, we can write a loop that follows the next fields, as shown below:

// Initialize the loop by determining a starting Node:
//
Node node = ... ;

// Continue looping while we have a valid Node reference:
//
while (node != null) {

    // Get the item from the current Node and compute with it:
    //
    Object item = node.getItem() ;
    /* ... */

    // Advance to the next Node by "following"
    // the "next" field in the Node:
    //
    node = node.getNext() ;
}
Ok, this type of loop works fine for us, but Node is only package access and we don't want to publicize it to the outside world. Why? Because there are dozens of ways to implement lists and we want to keep the freedom to change our implementation! Now, if we keep access to Node restricted to the package, that means that other programmers can't access it. How, then, could they write a loop like the one above? They can't and, even worse, they don't even have the access required to write recursive code. Second, we don't want to force our users to use recursion, anyway, because recursion is often less efficient, especially in space (why?), than loops. We don't know the efficiency requirements of our users because, remember, our lists are generic and will be used for lots of different applications. So, for the next building block, the goal is to implement a publicly accessible class that encapsulates the computations involved in looping through a sequence of Node objects.

  1. (15 points) In package edu.utexas.cs303e.student.utility, implement a public class named Cursor that provides a view of a Node as follows: Put your source code for Cursor into a file named Cursor.java and submit it via turnin.

    With the Cursor, it now becomes possible to loop through the items in a sequence of Node objects with the following type of code (test this!):

    Cursor cursor = new Cursor (node) ; // node already defined.
    while (cursor.hasNext()) {
        Object item = cursor.next() ;
        // do something with item ...
    }
    
    Even better, Cursor is public to our users, yet it also hides the implementation of Node! Therefore, if we want to change our Node implementation in a later release of our software, we can do so as long as we provide an updated version of Cursor for our users.

Ok, both Node and Cursor have various elements with only package access. So, how does a user get to use them? The next step (and last, for this assignment) is to implement a wrapper class that is public and that combines Node and Cursor objects to let our users create and process items in lists.

  1. (60 points) Implement a public class named LinkedList, with public access, in package edu.utexas.cs303e.student.utility, that has two private instance variables: A Node and an int size. The Node field, when non-null, references the first Node in a sequence of Node objects that contain the items in a list. The size field holds the number of Node objects that are in that sequence and, therefore, the size will be zero if the Node field is null. As the LinkedList is mutated, the size field must always be kept up-to-date. (Why do you think the name of this class is LinkedList?) Your class should have the following constructors and methods:

    Put your implementation into a file named LinkedList.java and submit it via turnin.

    That's the only implementation that you're asked to do for LinkedList, but you can undoubtedly see that there are other methods that could be useful (like deleting an item). Why not think of a few useful methods and consider how you'd implement them? Those methods that mutate the list, other than by adding, are the ones can may seem difficult to implement. How can you implement these without making Node mutable?

Consider, for future work, how you might extend LinkedList to make other useful classes. For example, how could you implement a different version of your Phrase class using a LinkedList as a base?

As a more generic example, how would you implement a set structure? The basic operation required for a set is the boolean ∈ operation (often named contains or isElementOf) that returns true exactly when an element, specified via a parameter, is in the set. Further, if an element is in a set, it can only be in the set once. However, a list can have as many copies of an element as desired. So, how would you implement the add operation for a set to ensure that no element can be added more than once? As examples of more advanced operations, how would you implement set union and set intersection?