Go Assignment 3

Go Mad

Assigned
Due

Feb 13
95 pts
Mar 6

Write a random word-replacement program, similar to the mad-lib game which children enjoy. The input consists of a series of word lists, followed by some text into which words are substituted from the lists at random.

Input Format

The input is a series of words. A word is a group of non-blank characters separated from the others by blank characters. Blank characters are spaces, tabs and newlines. The input consists of two sections, separated by a colon. The first section specifies the replacement words, the second section is copied to output, with some words replaced.

The first section is a series of one or more word lists, each list consisting of one or more words. The lists are separated by semicolons.

The second section is a series of words which are are copied to the output, some of which after replacement by a random word from the substitution list. Words which begin with the number sign (#) are to be replaced, others are not. The number sign is followed by a number which refers to one of the lists from the first section, numbered starting from zero through the number of lists less one. This word should be replaced by one chosen randomly from the list given by the number. If the number is out of range, substitute the word [error] instead of choosing from a list.

Note that the semicolon and colon separators must be independent words; they cannot be part of a larger group. For instance, goobers: is just an ordinary word in the word list. It does not end the section. goobers : is two words, the first is the last word in the last list, since the colon then ends the section.

For instance:

Mike Bill Susie Fran Bill Alex ; ran walked drove ; store house boat dock 'fridge ; duck dog filbert computer generator ; red blue avian international insane emancipated arbitrary ; wings ears feet fingers eyes nodules elbows : #0 #1 to the #4 #2 . Inside, there was a large #3 with #4 #5 .

Note: You must read from standard input. Your program may not open a file. (But see below for a more convenient way to provide input without typing it in each time.)

Operation

The program should read and store the replacement section without producing any output. It should then copy the words in the second section to the output. Ignore the line breaks in the input, and write the output with twelve words per line, separated by spaces. The last line, of course, may have fewer words.

When choosing replacement words, a word should be chosen from the correct list with equal probability for each word. In general, each run of the program will produce different output. Some possibilities from the above input are:

Bill walked to the insane house . Inside, there was a large generator with red ears .
or
Susie ran to the blue dock . Inside, there was a large dog with emancipated wings .

Picky Details

Any word in the second section that starts with # is a replacement word. The # should be followed by a number. Ignore any characters which may follow the number. Use this rule even if the number is empty. That means #5dog3 is equivalent to #5, and #broken is equivalent to #0. This rule specifies behavior for some flaky input, and is easy to implement since it follows the behavior of the fmt.Sscan library function. You can recover the list number with an expression like:

fmt.Sscan(word[1:], &listno)
Here, we're using the library Sscan function to read the string as though it were input. The word read from input, such as #12 is stored in the string variable word, and the integer result is stored in listno, which should be a integer variable. The notation word[1:] takes a slice of the string, which is effectively a substring.

The # character is special only at the start of a word. A # elswhere in the word does not designate a replaceable word.

Do not substitute words in the substitution lists. That is, #3 specifies a substitution when it appears in the second section. If it appears in the first, it's just a word that can be printed if the random process selects it.

Hints

The program is designed so that you can read the whole input using calls like fmt.Scan(&word), where word is a string variable. This creates some awkwardness, for instance when punctuation appears immediately after a substitution, or the need for spaces around the input separators, but I think we can live with it.

I stored my word lists in dynamically-allocated arrays built using a slice and the append function, much like the input loop in the bubble sort example. Whenever it was time to start a new word list, I used the make function to create a nice new slice of strings. Go creates the underlying array for me, and I just use append to add new strings to the list. Go grows the array as needed.

I used a similar technique to keep the list of word lists, but here the array contains slices. Again, I used make to allocate a [][]string, that is, a slice of slices of string. Each time an individual word list was completed, I appended the slice representing that word list to the slice representing the list of lists.

The broad structure of my program has two loops, a list-reading loop that reads the substitution words and creates the lists, followed by a copy loop that reads the words in and prints them with appropriate substitutions. Both loops operate much like the input loop of the bubble sort, except reading in strings. The body of the first one looks for the special symbols to decide if the word should be appended to the current list, if we need to start the next list, or to leave the loop.

The second loop reads and copies the input words, checking which need substitution, and printing a word from the appropriate list, or just echoing the word. Go strings can be sub-scripted like arrays, which gives an easy way to see if the first character is a #. If it is, you can extract the number using fmt.Sscan as described above.

Note the requirement that the input line breaks are ignored, and the output should be given with 12 words per line. I expect you can manage a counter to accomplish this.

For choosing words at random, you will want to use Go's rand package. You will find the first example helpful. You will want to seed the random number generator with a value that changes; the comment in that example suggests a simple solution. (You will need to add the time package to your imports to make that work.)

You might consider not adding this seed call initially, since programs are usually easier to debug when they don't produce different output each time.

Piping In The Bits

When running a program from the command line, the program reads standard input from the console by default. You can change this by specifying the input file using the < operator. For instance
[tom@laptop asst]$ mad < madin.txt Fran walked to the international store . Inside, there was a large dog with emancipated nodules . [tom@laptop asst]$
This also works fine from the Windows command window.

Submission

When your program works, is well-commented, and nicely indented, submit over the web here.