Graphic by Keith Ohlfs
CS111, Wellesley College, Spring 2002

Problem Set 7

Due Friday, April 5, at 11:59pm

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

Reading Assignment:

About this Problem Set

The purpose of this problem set is to give you a better understanding of lists via drawing box-and-pointer diagrams and writing recursive list methods. Task 1 is a pencil-and-paper problem in which you will draw an invocation tree for a recursive function that manipulates integer lists. In Task 2, you will write several methods that manipulate lists of strings as part of implementing an "unjumbling assistant" that will help you solve "word jumble" problems. The code for Task 2 is available in the ps7_programs folder in the cs111 download directory on nike.

How to turn in this Problem Set

You are required to turn in both a hardcopy and a softcopy. For general guidelines on problem set submission, including how to submit a softcopy and how to check if you softcopy submission was successful, 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).

Hardcopy Submission

Your hardcopy packet should consist of:
  1. The cover page;
  2. Your invocation tree/box-and-pointer diagram from Task 1;
  3. Your modified Unjumbler.java file from Task 2.
Staple these together, and slide the packet under the door of Elena's office (E127, in minifocus).

Softcopy Submission

Save the modified Unjumbler.java files in the Unjumbler folder of ps7_programs. Submit your final Unjumbler folder to your drop folder on the cs111 server. Submit only your Unjumbler folder. Do not submit the dicts folder or the UnjumblerTest folder.


Task 1: Box-and-Pointer Diagrams

In this problem, we first present some notational conventions for using invocation trees together with box-and- pointer diagrams for lists, and then ask you to use these conventions to show the execution of a list manipulation program.

Notational Conventions

Recall that an invocation tree is an abbreviation for a collection of Java Execution Model frames. Each node of the tree shows the arguments and the result of a method invocation; such a node abbreviates a frame in ExecutionLand. Also recall that a box-and-pointer diagram is an abbreviation for interconnected linked list nodes in ObjectLand.

It is possible to combine invocation trees and box-and-pointer diagrams to summarize the execution of a list manipulation program. For instance, consider the following definition of a list reversal method (which is assumed to be in a subclass of IntListOps, so that it "understands" append()):

public static IntList reverse (IntList L) {
  if (isEmpty(L)) {
    return L;
  } else {
    return append(reverse(tail(L)), 
                   prepend(head(L), empty()));
  }
}

Suppose that L1 is a list whose printed representation is [1,2,3]. Then the execution of the invocation reverse(L1) is summarized by the following diagram:

A boxed element of the form reverse(X):Y is the root node of an invocation tree for the invocation of the reverse() method on list X that returns result Y . In the invocation tree, particular list nodes are denoted by object references, which are lowercase letters in circles. These object references name nodes appearing in the box-and-pointer portion of the diagram. The object references could have instead been denoted by pointers in the diagram, but this could lead to a spaghetti of pointers. We follow the convention that the empty list is always written as a distinct solid circle. (We are not interested in modelling sharing involving the empty list.)

In the above diagram we have decided to treat append() as a "primitive" by not showing nodes for each invocation of append(). However, the nodes created by the prepend()s performed by the hidden append()s are shown in the box-and-pointer diagrams.

An important feature of the box-and-pointer list diagram portion of the picture is that it accurately depicts sharing. In the case of reverse(), the result returned by one call to reverse does not share any list nodes with the results of any other call. As an example of a diagram with more sharing, consider the following method, which is also assumed to be defined in a subclass of the IntListOps class:

public static IntList appendTails (IntList L) {
  if (isEmpty(L)) {
    return L;
  } else {
    return append(L, appendTails(tail(L)))
  }
}

Below is a diagram showing an invocation tree and box-and-pointer list structure for the invocation appendTails(L1), where L1 is defined as above. Note how the final result (the node labelled"f") includes as subparts the results to the recursive calls (the nodes labelled "e" and "d").

Your Task

Suppose that the following methods are defined in a subclass of the IntList class:

public static IntList F (IntList L) {
  return tail(G(0, L));
}
    
public static IntList G (int n, IntList L) {
     if (isEmpty(L)) {
       return prepend(0, L);
  } else {
    IntList subResult = G(n + head(L), tail(L));
    return prepend(head(L) + head(subResult),
                    prepend(head(L) * (n + head(subResult)),
                            tail(subResult)));
  }
}

Suppose that L2 is an integer list whose printed representation is [1,2,3,4]. Draw a combined invocation tree/box-and-pointer diagram that illustrates the invocation F(L2). Use the same conventions used in the reverse() and appendTails() examples from above in your diagram.


Task 2: Word Unjumbler

Overview

Word Jumble is a popular game that appears in many newspapers. There is now even an on-line version of Word Jumble. You may want to play this game a few times before continuing with the assignment.

The game involves "unjumbling" English words whose letters have been reordered. For instance, the jumbled word ytikt can be unjumbled to kitty. The game can be challenging; even relatively short jumbles can be tricky to unjumble. For instance, here are the words that appeared on the March 27, 2002, version of the on-line game; can you unjumble them?:

eshou, nabor, dyomle, trawey, oebrywe

In this problem, you will create a Java program that acts as an "unjumbling assistant". Given a string, your program will first generate all possible reorderings of the letters in the string. Such reorderings are called permutations. For example, there are six reorderings of the letters in the string tra:

[tra,rta,rat,tar,atr,art]

In general, a string with n distinct letters has n! (pronounced "n factorial") permutations. For instance, a 4-letter string has 4! = 24 permutations, a 5-letter string has 5! = 120 permutations, a 6-letter string has 6! = 720 permutations, and so on.

Next, the assistant will determine which of the permutations is an English word by looking them up in a "dictionary". You do not have to worry about how to construct such a dictionary; this has been done for you. Notes on how to use the dictionary as a "black box" can be found later in this problem description. In the case of "tra", filtering out the English words leaves:

[rat,tar,art]

When the string you are unjumbling contains duplicate letters, it turns out that a simple permutations generator will yield some duplicate permutations. For instance, the permutations of "dda" will generate the 3! = 6 permutations:

[dda,dda,dad,dad,add,add]

Filtering out the English words yields:

[dad,dad,add,add]

In such cases, the unjumbling assistant should also filter out duplicates to yield the final list:

[dad,add]

To get a feel for what the unjumbling assistant does, you should experiment with the working unjumbler test program in the folder UnjumblerTest within the ps7_programs folder. You can use the program to help you solve the on-line Word Jumble puzzles!

Your Task

Your task is to create your own unjumbler program, mostly from scratch. To do this, "all" you need to do is to write definitions for the nine class methods specified below.

Here are some things you need to know:

  1. public static StringList remove (String s, StringList L)
    Returns a new list in which all occurrences of s in L have been removed. The other strings in the list should have the same relative order in the resulting list as in the given list.

    Examples:
    remove("I", fromString("[I,know,that,I,said,that,I,did]")))
          returns the string list [know,that,said,that,did].

    remove("that", fromString("[I,know,that,I,said,that,I,did]"))
          returns the string list [I,know,I,said,I,did].

    remove("said", fromString("[I,know,that,I,said,that,I,did]"))
          returns the string list [I,know,that,I,that,I,did].

    remove("you", fromString("[I,know,that,I,said,that,I,did]"))
          returns the string list [I,know,that,I,said,that,I,did].

    Note: Use the equals instance method from the String class to compare two strings. For instance, "cat".equals("cat") returns true but "cat".equals("dog") returns false. You should not use == to compare two strings because it may not return what you expect. For instance, while "cat" == "dog" is guaranteed to return false, "cat" == "cat" and "cat" == ("c" + "at") are not guaranteed to return true. They may return true in some implementations and some circumstances, but you cannot rely on this behavior.

  2. public static StringList removeDuplicates (StringList L)
    Returns a list containing each string in L exactly once. The order of the elements in the returned list should be the relative order of the first occurrence of each element in L.

    Examples:
    removeDuplicates(fromString("[I,know,that,I,said,that,I,did]"))
          returns the string list [I,know,that,said,did].

    removeDuplicates(fromString("[you,say,what,you,mean,and,mean,what,you,say]"))
          returns the string list [you,say,what,mean,and].

    removeDuplicates(fromString("[lists,are,cool]"))
          returns the string list [lists,are,cool].

    Note: The remove method from above is helpful here!

  3. public static StringList mapConcat (String s, StringList L)
    Given a list L with n strings, returns a new list with n strings in which the ith string of the resulting list is the result of concatenating s to the ith element of L.

    Examples:
    mapConcat("com", fromString("[puter,plain,municate,pile]"))
          returns the string list [computer,complain,communicate,compile].

    mapConcat("I ", fromString("[came,saw,conquered]"))
          returns the string list [I came,I saw,I conquered].

  4. public static StringList insertions (String s1, String s2)
    Given two strings s1 and s2, where s2 has n characters, returns a list of n + 1 strings that result from inserting s1 at all possible positions within s2, from left to right.

    Examples:
    insertions("*", "split")
          returns the string list [*split,s*plit,sp*lit,spl*it,spli*t,split*]

    insertions("a", "bcd")
          returns the string list [abcd,bacd,bcad,bcda]

    insertions("com", "[pile]")
          returns the string list [compile,pcomile,picomle,pilcome,pilecom]

    insertions("abc", "")
          returns the string list [abc]

    Note: The Lab8Ops class contains two helper methods that are useful for defining insertions:

    1. public static String first (String s)
      Returns a string consisting of the first character of s. For example, first("computer") returns the string "c".

    2. public static String butFirst (String s)
      Returns a string consisting of all but the first character of s. For example, butFirst("computer") returns the string "omputer".

  5. public static StringList insertionsList (String s, StringList L)
    Returns a list that contains all the string that result from inserting s at all possible positions in all the strings of L.

    Examples:
    insertionsList("a", fromString("[bc,cb]"))
          returns the string list [abc,bac,bca,acb,cab,cba]

    insertionsList("*", fromString["I,am,Sam"])
          returns the string list [*I,I*,*am,a*m,am*,*Sam,S*am,Sa*m,Sam*]

    insertionsList("abc", fromString("[]"))
          returns the string list []

    Note: The StringListOps class contains a helper method append that is useful for defining insertionsList:

    public static StringList append (StringList L1, StringList L2)
    Returns a new string list containing all the elements of L1 followed by all of the elements of L2. For example,
    • append(fromString("[I,do]"), fromString("[not,like,green,eggs]")) returns [I,do,not,like,green,eggs]
    • append(fromString("[I,do]"), fromString("[]")) returns [I,do]
    • append(fromString("[]"), fromString("[not,like,green,eggs]")) returns [not,like,green,eggs]

  6. public static StringList permutations (String s)
    Returns a list of all permutations of the string s. A permutation of a string s is any string that is formed by reordering the letters in the string s (without duplicating or deleting any letters). For a string with n distinct characters, there are exactly n! (i.e., "n factorial") permutations. If some characters in s are repeated, there are still n! permutations, but the permutations contain duplicates.

    Examples:
    permutations("a") returns the string list [a].

    permutations("ab") returns the string list [ab,ba] or the string list [ba,ab].

    permutations("abc") returns (any permutation of) the string list [abc,bac,bca,acb,cab,cba].

    permutations("abcd") returns (any permutation of) the string list

          [abcd,bacd,bcad,bcda,
           acbd,cabd,cbad,cbda,
           acdb,cadb,cdab,cdba
           abdc,badc,bdac,bdca,
           adbc,dabc,dbac,dbca,
           adcb,dacb,dcab,dcba]

    permutations("121") returns (any permutation of) the string list [121,211,211,112,112,121]. Note that when the given string contains duplicate characters, the permutation list will contain duplicates.

    permutations("1231") returns (any permutation of) the string list
           1231,2131,2311,2311,
           1321,3121,3211,3211,
           1312,3112,3112,3121,
    
           1213,2113,2113,2131,
           1123,1123,1213,1231,
           1132,1132,1312,1321]

    Note: There are many ways to define the permutations method, but a particularly elegant way uses the first, butFirst, and insertionsList methods from above. Be very careful in defining your base case!

  7. public static StringList filterWords (StringList L)
    Returns a list of all strings in L that are English words. The resulting strings should be in the same relative order as in L.

    Examples:
    filterWords(fromString("[the,dog,barked,at,the,cat]")) returns the string list [the,dog,barked,at,the,cat].

    filterWords(fromString("[the,dog,barkd,ate,hte,cat]")) returns the string list [the,dog,ate,cat].

    filterWords(fromString("[tra,rta,rat,tar,atr,art]")) returns the string list [rat,tar,art].

    Note: To determine if a string is an English word, you should use the class method isWord that is already defined for you in the Unjumbler class:

    public static boolean isWord (String s)
    Returns true if s is a word in the default English dictionary, and false otherwise.

    The default dictionary (which can be changed within the Unjumbler class) contains 22641 English words (all lower case, no proper nouns) of up to 8 characters in length. It is not a "perfect" dictionary: there are some perfectly acceptable English words that are not in the dictionary.

  8. public static StringList unjumble (String s)
    Returns a list of all the permutations of s that are English words (as determined by the default dictionary). The order of elements in the resulting list does not matter, but each word in the resulting list should listed only once.

    Examples:
    unjumble("tra") returns the string list [rat,tar,art].

    unjumble("tras") returns the string list [rats,arts,star]
          (the default dictionary doesn't recognize tars or tsar as words).

    unjumble("argle") returns the string list [glare,large,lager,regal].

    unjumble("sbso") returns the string list [sobs,boss].

    Note: You should only remove duplicates after performing filterWords. It turns out that performing removeDuplicates on a large lists (such as the output of permutations can take a very long time. (To understand why this is so, take CS230!)

  9. public static void unjumbleInteractively()
    Launches an interactive session in the Java Console that repeatedly prompts the user for a string and displays the list of unjumbled English words for that string. The interactive session ends when the user enters the empty string.

    Example: Below is a sample session of unjumbleInteractively(). The text typed by the program is in italics, while the text typed by the user is in bold:

    Enter a string to unjumble and press Return (enter the empty string to quit):
    argle
    Constructing dictionary from file ../dicts/dict8.txt.
    This may take a little while...
    Done! Dictionary constructed with 22641 words.
    [glare,large,lager,regal]
    Enter a string to unjumble and press Return (enter the empty string to quit):

    eshou
    [house]
    Enter a string to unjumble and press Return (enter the empty string to quit):

    sbos
    [boss,sobs]
    Enter a string to unjumble and press Return (enter the empty string to quit):

    Thank you for using the unjumbler!

    Note: To read input from the Java Console, use the following class method from the Stdin class:

    public static String readLine (String s)
    Displays the prompt s in the Java Console window, and waits for the user to type a line of text. When the user presses the return key, returns the line of text that has been typed as a string.