CSc 220 Assignment 4

Speak To Me At Random

Assigned
Due

Oct 6
90 pts
Oct 24

For this program, we will extend and improve the last one. You may start with your solution, or you may start with posted key. (Note that keys are only available from on-campus addresses.) You make these changes:

Behavior

The first part of your input is similar to the the last program. It consists of a series of lists, where each list starts with a name ending in a colon. A name can be any string of alphanumeric characters; the colon is not part of the name. Following the name is a list of words. Each word may be any non-blank string, so long as it does not end in a colon. The series of lists ends with an isolated colon as before. A list name may appear more than once, and the list simply consists of all the words following each appearance of the name; that is, a second use of a list name simply adds more words to that list.

The balance of the file is series of words which should be copied to the output, with substitutions. Words which start with a @ are special, other words are to just be copied to the output. Special words take three forms.
  1. @wordlist
  2. @savename=wordlist
  3. @savename
In the first form, the @ is followed by the name of one of the word lists. It should be replaced with a word chosen at random from wordlist. After a word is chosen and printed, the word should be removed from the list so it can't be selected again. If the list wordlist does not exist, or if the list is empty, simply omit the word on output. The second form is identified by the presence of =. You select a random word as in the first form, but also remember it under the savename. Only one word is remembered under any particular savename, so only the last setting is remembered. The third form uses a saved name. No random selection; simply substitute whatever was remembered and saved from before. Since the first and third forms look the same, the rule is to check the list of saved names first, and assume third form if one is found. If not, assume first form and check for a list name. (That means that if a form two word saves under the same name as a list, that list cannot be used in a type one afterwards.)
Read each word, substitute as required above, then print it (original or substitution). Since we are processesing input as words, we are discarding input line breaks and spacing. To keep the output from looking too ugly, use the following rules when printing the words:
  1. Keep a count of the number of words currently on the line. Just initialize it to zero, increment it when a word is printed, and set it back to zero when a newline is printed.
  2. Before printing a word, if that word starts with an alphanumeric character, print a separator first. If the number of words on the line is more than eight, the separator should be a line break, otherwise, if the count is greater than zero, separate with a space.
  3. As the very last thing in your program, print a newline so the input is always terminated with one.
These rules are designed to be (not too) complicated, keep the output line length reasonable and the words separated, except that commas and periods will attach to the words before them. These goals are limited, and it's not too hard to invent input that will make the output look ugly anyway.
So, given this input:
PN: Phil Mike Joe Sam Sue Alice Mary Lisa PASS: gave handed transmitted TOSS: threw pitched tossed lobbed OBJ: hammer rock watch flower document : @fn=PN @PASS the @OBJ to @PN , who @TOSS it back at @fn .
various possible outputs might be:
desktop$ ./gensub < in.txt Sue handed the rock to Joe, who tossed it back at Sue. desktop$ ./gensub < in.txt Alice gave the rock to Sue, who tossed it back at Alice. desktop$ ./gensub < in.txt Mike handed the rock to Lisa, who lobbed it back at Mike. desktop$ ./gensub < in.txt Phil handed the rock to Lisa, who pitched it back at Phil.
Though this example uses upper-case listnames and a lower-case savename, you should not depend on this in your program.

General Advice

As noted above, you may start with your own solution to asst 3, or you may start with mine. In either case, you will want to use a map of string to vector of string, which can hold any number of word lists. Rather than a separate variable for each list, each list name is mapped to its list. Your main loop is similar to assignment 4, but instead of needing an if to decide which list to read the contents in to, you read into a position in the map.

The second part of the program reads the remaining input words and performs the translation. You might want to use a structure something like this:
std::string word; while(std::cin >> word) { if(word[0] == '@') { // Find the correct substitution of word, and assign word // to its replacement, else run continue if there is // no replacement. } // Print a space or newline before the word if the rules above // require it. // Print word. }
You will have the map of string to vector of string holding the word list and filled up in the first part. You will also want to create a map of string to string to hold the assignments made by type two replacements described above. Unlike the wordlist structure, the assignment structure is empty when this second loop starts.
When you enter the if in the loop, you may want to do the following
  1. Use string operations to remove the leading @ from word.
  2. See if word contains a =. If not, it's a type one or three. Check the assignment map for the key word.
    1. If word is present in the assignment map, assign its value part to word.
    2. If not, look for word in the word list map. If found, choose a word at random and remove it from the list. That will become the new value of word.
    3. If not found in either map, this is a bad work. Just continue.
  3. Else, if the original word does contain =, it's a type 2. Remove and store the savename part from the front of word (and discard the =). Process the remaining word as a type 1, with the additional step of adding the chosen word to the assignment map under the specified savename.

When you want to tell if a character is alphanumeric, you might use isalnum from the library.

Life will probably be easier if you create a helper function that takes a vector of strings, chooses one at random and removes it from the list, then returns the chosen string.

There are a couple of ways to remove the selection from a vector. Of course, first choose the location at random then store the data there in a temporary variable, then remove the data in the selected positition from the vector. Vector has an erase method which will do the trick, or you can assign the last item of the array into the selected position, then use pop_back to remove it. The later is probably more efficient. It reorders the vector, of course, but that doesn't matter for this application.

Submission

When your program works, and is properly formatted and commented, submit over the web here.