Go Assignment 3

Go Mad

Assigned
Due

Feb 17
95 pts
Mar 10

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 replacements.

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. The list number may be followed (as part of the same word) by one or more characters stating with a non-digit. These are to be retained and will follow the replacement word.

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 Josie ; ran walked drove rolled ; store house boat dock 'fridge TV ; duck dog generator boiler wombat buick eagle lawyer omnibus ; red blue avian international insane emancipated arbitrary obligatory ; wings ears feet fingers eyes nodules elbows claws : #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 dock. Inside, there was a large generator with blue elbows.
or
Susie rolled to the international house. Inside, there was a large duck with obligatory wings.

Picky Details

Any word in the second section that starts with # should be a replacement word. The # should be followed by a number, and optionally additional characters. If the count is missing, such as the input #hithere, it is an input error, and you may handle it any way you think best. After reading a scan word into word and noticing that it starts with #, I took the word apart with an expression like:

fmt.Sscanf(word[1:], "%d%s", &listno, &rest)
Here, I'm using the library Sscanf function to read the string as though it were input, and extracting the count and any extra which follows it. 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.

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.