Graphic by Keith Ohlfs
CS111, Wellesley College, Fall 2001

Problem Set 7

Due on Tuesday November 6, at 11pm

[CS111 Home Page] [Syllabus] [Assignments] [Documentation] [FAQ] [CS Dept.] [CWIS]


Reading:

About this Problem Set

The purpose of this problem set is to give you a better understanding of lists by writing recursive list methods. There are two problems on this assignment and each problem has several parts.

How to turn in this Problem Set

Submit the folder ps7_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).



Task 1: Recursive ObjectList Methods

In this problem, you are to implement the ObjectList methods specified below. Write all your code for this problem in the file ps7ObjectListOps.java within ps7_programs.

Run the project with the file ps7ObjectListOpsTest.html in the topmost position in LinkOrder to test your solutions. This applet runs your implementations of the methods on several test cases, and writes the answers to the standard output window (stdout). Study these answers to make sure that your methods have the correct behavior. If you want to print out the contents of the stdout window, you first need to save it away as a file. To do this, open the pull down menu under the apple in the upper left-hand corner of the screen, select Java Runtime, and then select Save Text Window. You will be prompted for the name of a file. Once the window is save to a file, you can open the file and print it.

When giving examples below, we will assume the existence of the following test lists, each of which is a list of strings:

L0 = [ ]
 
L1 = [Hello, there]
 
L2 = [How, are, you]
 
L3 = [I, am, what, I, am, you, are, what, you, are]
Part a isMember()
public static boolean isMember (Object x, ObjectList L)
Returns true if x is in L and false otherwise. Uses .equals() to test for equality of strings, not ==.

For example:

isMember("I", L0) = false
isMember("are", L0) = false
isMember("you", L0) = false
isMember("I", L1) = false
isMember("are", L1) = false
isMember("you", L1) = false
isMember("I", L2) = false
isMember("are", L2) = true
isMember("you", L2) = true
isMember("I", L3) = true
isMember("are", L3) = true
isMember("you", L3) = true
Part b remove()
public static ObjectList remove (Object x, ObjectList L)
Returns a list containing all the elements of L (in the same order) except for occurrences of x, which have been removed. Uses .equals() to test for equality of strings, not ==.

For example:

remove("I", L0) = [ ]
remove("are", L0) = [ ]
remove("you", L0) = [ ]
remove("I", L1) = [Hello, there]
remove("are", L1) = [Hello, there]
remove("you", L1) = [Hello, there]
remove("I", L2) = [How, are, you]
remove("are", L2) = [How, you]
remove("you", L2) = [How, are]
remove("I", L3) = [am, what, am, you, are, what, you, are]
remove("are", L3) = [I, am, what, I, am, you, what, you]
remove("you", L3) = [I, am, what, I, am, are, what, are]
Part c removeDuplicates()
public static ObjectList removeDuplicates (ObjectList L)
Returns a list containing a single occurrence of each elements occuring in L. The order of the resulting elements is irrelevant. Uses .equals() to test for equality of strings, not ==.

For example (note, the strings in the lists could be in any order, not necessary the order shown below):
removeDuplicates(L0) = [ ]
removeDuplicates(L1) = [Hello, there]
removeDuplicates(L2) = [How, are, you]
removeDuplicates(L3) = [I, am, what, you, are]

Part dinterleave()
public static ObjectList interleave (ObjectList L1, ObjectList L2)
Returns a list of the elements of L1 interleaved with the elements of L2. The resulting list should contain the first element of L1 followed by the first element of L2, followed by the second element of L1, followed by the second element of L2, etc. When one of the lists runs out of elements, all the elements of the other list should be included.

For example:

interleave(L0, L0) = [ ]
interleave(L0, L1) = [Hello, there]
interleave(L1, L0) = [Hello, there]
interleave(L1, L2) = [Hello, How, there, are, you]
interleave(L2, L1) = [How, Hello, are, there, you]
interleave(L2, L2) = [How, How, are, are, you, you]
interleave(L2, L3) = [How, I, are, am, you, what, I, am, you, are, what, you, are]
interleave(L3, L3) = [I,I, am, am, what, what, I, I, am, am, you, you, are, are, what, what, you, you, are, are]
Part ecountOccurrences()
public static int countOccurrences (Object x, ObjectList L)
Returns the number of occurrences of x as an element of L. Uses .equals() to test for equality of strings, not ==.

For example:

countOccurrences(I, L0) = 0
countOccurrences(I, L1) = 0
countOccurrences(How, L2) = 1
countOccurrences(are, L3) = 2

Part f reverse()
public static ObjectList reverse (ObjectList L)
Returns a list of the elements of L in reverse order. The last element of L is the first element of reverse(L), the next-to-last element of L is the second element of reverse(L), and so on.

For example:

reverse(L0) = [  ]
reverse(L1) = [there, Hello]
reverse(L2) = [you, are, How]
reverse(L3) = [ are, you, what, are, you, am, I ,what, am , I]


Task 2: Doctor

In this problem, we will experiment with a very simple program that simulates a nondirective psychotherapist. This program is inspired by the celebrated Eliza program created by Joseph Weizenbaum at MIT in the early 1960s. Eliza was realistic enough to fool some people into thinking that they were conversing with another person, not a computer. Eliza has inspired a long line of programs that have grown in sophistication over the years.

A description of the doctor program follows. You should try out the program by executing appletviewer Doctor.html. You will quickly see that no one could be fooled into thinking that this program is a real human being. You will be working on making a few improvements to the program. Your changes will make the doctor a bit more human. A lot more work would be required to change the program to the point where it would fool people.

Overview of the Doctor program

A patient engages in a "conversation" with the doctor program by typing sentences into the input field of the Doctor applet. These sentences are echoed in the text area of the applet, along with the doctor's replies. Below is a sample conversation that could take place in the program. For readability, the doctor's replies have been italicized, though in the actual program all sentences are in the same font.
Doctor: What is your name?
   Patient: Chandler
   Doctor: Hello, Chandler. What seems to be the trouble?
   Chandler: Joey moved out
   Doctor: Why do you say Joey moved out
   Chandler: He moved to another apartment
   Doctor: You seem to think he moved to another apartment
   Chandler: Yes, he moved out last week
   Doctor: Please continue.
   Chandler: I miss him
   Doctor: Many people have the same sorts of feelings.
   Chandler: Well I guess I can still see him
   Doctor: Earlier you said that he moved to another apartment
   Chandler: Yeah, but he doesn't live too far away
   Doctor: Please continue
   Chandler: And then there's the business with Janice
   Doctor: Why do you say and then there's the business with Janice
   Chandler: Goodbye
   Doctor: Goodbye, Chandler. See you next week!
The doctor program uses three simple strategies to respond to a patient:
  1. Use a hedge statement like "Please continue" or "Many people have the same sorts of feelings".
  2. Attach a qualifier like "You seem to think" or "Why do you say" to the patient's most recent statement.
  3. Attach a qualifier like "Earlier you said that" or "Before you mentioned" to one of the patient's previous statements.
These strategies are embodied in the reply() method of the Doctor class which takes the patient's most recent statement and a list of all the patient's previous statements to decide how it should reply. It is important to understand that you are NOT going to be writing the basic doctor program. We are giving that to you! If you are curious, you can read the comments in the source to understand how it works. For this problem, you will simply be making modifications to the basic program. You do not need to understand the details of the program in order to make the requested modifications.


Part a -- Substitutions

The way the Doctor uses qualifiers is too simplistic to fool even the most gullible person. For example, if the patient says "I am unhappy with how they treated me", the doctor responds by prefacing the same exact string prefixed with a qualifier --- e.g., "You feel I am unhappy with how they treated me".

A better approach would be to change various words in the response to indicate the change in person. The above response would be more realistic as "You feel you are unhappy with how they treated you".

Here is a table of the type of substitutions we want to add to the doctor program:
 

old world new word
I you
me you
my your
mine yours
am are
you I
are am

Note that the first five substitutions are always valid, but the last two can sometimes be invalid. The word "you" could translate to either "I" or "me". For this problem we will "guess" it should be "I", but we could be wrong. We could also be wrong in the following situation: "are" could be a verb for the third person plural, as in "Clouds are pretty", in which case we really don't want to change it to "am". We would need a more sophisticated approach to handle these cases correctly.

The doctor program contains a parseWords() method that correctly parses a string into a list of strings that are its component words and punctuation marks. For example, if sentence is the string

"You know, I really miss him!"
then parseWords(sentence) yields an ObjectList of strings with the following printed representation:
[You, know, ,, I, really, miss, him, !]
(Here ",," should be interpreted as the punctuation string "," followed by a comma delimiting the components of the ObjectList. ) The "inverse" of parseWords() is unparseWords(), which glues a list of words and punctuation marks back into a sentence that is a single string.

Using these methods, we can define a changePerson() method that applies the above person-changing substitutions to a sentence:

public String changePerson (String sentence) {
  return unparseWords(substitute(substitutions, parseWords(sentence)));
}
Here, substitutions is a class variable of Doctor containing a list of substitution pairs that alternates between old words and the corresponding new words:
[I, you, me, you, my, your, mine, yours, am, are, you, I, are, am]
To make changePerson() work, you need to define the following two methods:
public static String getSubstitute (ObjectList subPairs, String word);
Assume subPairs is a list of strings with alternating "old"/"new" word pairs. If word is any "old" word in subPairs, returns the corresponding"new" word in the pair. Otherwise, returns the given word unchanged.

public static ObjectList substitute (ObjectList subPairs, ObjectList words);
Assume subPairs is a list of strings with alternating "old"/"new" word pairs, and words is a list of strings. Returns the list of strings that results from mapping the getSubstitute substitution process over every word in words.

Examples

Suppose we have the following test lists:

L0 = [ ]
L1 = [A, green, book]
L2 = [The, red, cat]
L3 = [Five, dog, days]
subPairs1 = [red, green, five, seven, green, yellow, cat, dog]
Then here are some test cases of these two methods:
getSubstitute(subPairs1, "red") = green
getSubstitute(subPairs1, "five") = seven
getSubstitute(subPairs1, "green") = yellow
getSubstitute(subPairs1, "cat") = dog
getSubstitute(subPairs1, "seven") = seven
getSubstitute(subPairs1, "dog") = dog
getSubstitute(subPairs1, "house") = house
   
substitute(subPairs1, L0) = [ ]
substitute(subPairs1, L1) = [A, yellow, book]
substitute(subPairs1, L2) = [The, green, dog]
substitute(subPairs1, L3) = [seven, dog, days]
Notes:

Part b -- Word Frequency Detection

Another way to make the doctor more sophisticated is to have the doctor notice when the patient keeps refering to the same words. For instance, if a patient says "I saw a cat in my dreams" and has mentioned "dreams" several times previously, the doctor could say "Several times now you have mentioned dreams" or "You seem to keep coming back to dreams".

Here is a method that can be used to detect words frequently used in a sentence:

public static ObjectList usedBefore (int minSize, int minOccur, 
                                     String sentence, ObjectList sentences) {
  // Assume sentences is a list of sentence strings.
  // Returns a list of the words in sentence that are at least
  // minSize long and are used at least minOccur times in sentences.
  ObjectList words = mapLowerCase(filterSize(minSize, parseWords(sentence)));
                     return wordsUsedBefore(minOccur, words, sentences);
}
The usedBefore() method depends on the following methods, which you will define:
public static ObjectList mapLowerCase (ObjectList L);
Assumes L is a list of strings. Returns a list of strings whose elements are the lowercase versions of the strings in L. Use the String instance method toLowerCase() to find the lowercase version of a string.

public static ObjectList filterSize (int wordSize, ObjectList L);
Assumes L is a list of strings. Returns a list, in the same order, of the strings in L whose lengths are at least wordSize characters. Use the String instance method length() to measure the length of a string.

public static ObjectList wordsUsedBefore (int minOccur, ObjectList words, ObjectList sentences)
Assume words is a list of word strings, and sentences is a list of sentence strings. Returns the list of words in words that appear in at least minOccur sentences.

For defining the methods above, it is also helpful to have the following auxiliary methods:
public static int occurrences (String word, ObjectList sentences);
Assume sentences is a list of sentence strings. Returns the number of sentences in which word appears.

public static boolean isMember (String word, ObjectList sentence);
Assumes sentence is a list of words. Returns true if the word is in the sentence, false otherwise. isMember is case insensitive.

Examples

Here are test cases involving isMember, mapLowerCase and filterSize, using the following lists:
L4 = [ ]
L5 = [You, are, not, LISTENING, to, me, !]
L6 = [I, am, what, I, am, ;, you, are, what, you, are]
L7 = [I, am, saying, that, my, goal, is, to, get, what, is, mine, .]
   
isMember("I", L4) = false
isMember("are", L4) = false
isMember("you", L4) = false
isMember("I", L5) = false
isMember("are", L5) = true
isMember("you", L5) = true
isMember("I", L6) = true
isMember("are", L6) = true
isMember("you", L6) = true
isMember("I", L7) = true
isMember("are", L7) = false
isMember("you", L7) = false
   
mapLowerCase(L4) = [ ]
mapLowerCase(L5) = [you, are, not, listening, to, me, !]
mapLowerCase(L6) = [i, am, what, i, am, ;, you, are, what, you, are]
mapLowerCase(L7) = [i, am, saying, that, my, goal, is, to, get, what, is, mine, .]
   
filterSize(3, L4) = [ ]
filterSize(4, L4) = [ ]
filterSize(5, L4) = [ ]
filterSize(3, L5) = [You, are, not, LISTENING]
filterSize(4, L5) = [LISTENING]
filterSize(5, L5) = [LISTENING]
filterSize(3, L6) = [what, you, are, what, you, are]
filterSize(4, L6) = [what, what]
filterSize(5, L6) = [ ]
filterSize(3, L7) = [saying, that, goal, get, what, mine]
filterSize(4, L7) = [saying, that, goal, what, mine]
filterSize(5, L7) = [saying]
Suppose we define testSentences as a list of the following sentences:
[Last night I had many dreams.,
 I pursue my dreams.,
 In my dreams, I win the lottery.,
 I like the night.,
 You are helpful.
 ]
Further suppose that we define testWords as a list of the following words:
[Last, night, I, had, many, dreams]
Then here are some test cases involving occurrences() and wordsUsedBefore():
occurrences("I", testSentences) = 4
occurrences("dreams", testSentences) = 3
occurrences("night", testSentences) = 2
occurrences("lottery", testSentences) = 1
occurrences("cat", testSentences) = 0
   
wordsUsedBefore(1, testWords, testSentences) = [Last, night, I, had, many, dreams]
wordsUsedBefore(2, testWords, testSentences) = [night, I, dreams]
wordsUsedBefore(3, testWords, testSentences) = [I, dreams]
wordsUsedBefore(4, testWords, testSentences) = [I]
wordsUsedBefore(5, testWords, testSentences) = [ ]