CS140 -- Lab 7
This lab will give you practice:
- using doubly linked lists,
- using lists of lists,
- writing programs to test library code,
- figuring out potential error conditions, and
- writing a design document. Before you start this lab, you will need to create and submit a design
document to the TA. The last section of this lab write-up describes what your design
document should contain.
Materials
You will need the following files:
- Executables for the answers are in the directory
/home/bvz/cs140/labs/lab7. As usual, if you have
questions about how these programs should work, try these.
- A file named dllist.c can be found in /home/bvz/cs140/labs/lab7.
You will need to copy this file to your directory and complete it, as
described in part 1.
- Sample files:
- A sample set of commands for the photo sorter application can be
found in photo_commands.
- A sample input file for linenum can be found in ice_cream.
You should make up your own sample data as well.
- You will need to include fields.h and dllist.h from
/home/bvz/cs140/include.
- You will need to link in fields.o from
/home/bvz/cs140/objs and dllist.o from your own directory.
If you are uncertain about whether or not your dllist library is doing
the right thing, you can also link in my dllist.o file from
/home/bvz/cs140/objs for testing purposes. Ultimately, however
you must link in your own dllist.o file.
- You must the interface for
my doubly-linked list library, dllist.h, for this lab, unless
otherwise noted.
Part 1 -- Completing the Dllist Library
As described in the class notes and in lecture, you need to write the
following four functions to complete dllist.c:
- dll_insert_after
- dll_prepend
- dll_append
- dll_delete
If you forget what these functions do or want some hints on how to implement
them, consult the dllist notes.
Part 2 -- Testing Your Dllist Library
When you write a library, like dllist, that will be used in multiple applications,
you should try to thoroughly test it before you release it. One way to test
your library is to write a driver application that tests all
the different functions in your library. You are going to write a driver
program named photo_sorter.c to test your dllist library.
photo_sorter is going to mimic an imaginary photo album in which you
are arranging the photos. Each photo is going to be identified by a single
word, and your driver program is going to accept a number of commands for
arranging the photos:
- append photo_name: appends the named photo to the end of the list.
- prepend photo_name: prepends the named photo to the beginning of the
list.
- after photo1 photo2: inserts photo2 after photo1 in the list.
- before photo1 photo2: inserts photo2 before photo1 in the list.
- delete photo_name: delete the named photo from the list.
An example set of commands might be:
append brad
append smiley
append fred
prepend joe
prepend yifan
after yifan ebber
before brad huck
before yifan bounce
delete joe
delete fred
after yifan wolf
Do not worry about duplicate photo names. If a duplicate occurs, you should
find the first photo with that name in the list and have the command act on that
photo.
Output
valid command: After each valid command
your program should print a list header and then
the contents of the list,
one photo per line. The list header should consist of:
- the command number
- a ":"
- a space
- the header "Photo List"
For example, after the first six commands above (ending
with "after yifan ebber"), your output would be:
6: Photo List
yifan
ebber
joe
brad
smiley
fred
invalid command: After an invalid command you should print an
appropriate error message, prefixed with the command number. If you experiment
with my executable, you can see the error messages that I used.
line spacing: There should be a single blank line after each list
or error message.
Implementation
Your program should:
- create a dllist and an inputstruct to read from stdin,
- read commands from stdin until EOF is received. Your program should
perform the appropriate action for each command. If the command is
a valid one, it should then iterate
through the list and print the names of the photos one per line.
Remember to print the list header before you print the list. You can
use the inputstruct's line field as the command number,
since there is one command per line, and therefore the line number is
the same as the command number. If the command is invalid, you should
print an error message and continue.
You
will need to include your dllist.o object file in the list of
files you use to build your photo_sorter executable.
When I was designing my solution to this problem, my strategy
for incremental development was to initially assume that the commands
were always correct, and to add error checking only after I had the
commands working properly. I then divided the commands into ones which
were easy to implement, namely prepend and append, and
the ones which would be more difficult to implement, namely after,
before, and delete. I implemented the easier ones first,
then the more difficult ones. The reason prepend and append
are easy is that you can simply call dll_prepend and dll_append
to implement them. In contrast, the remaining three commands require that
you traverse the list to find a target node, and then do something with
that target node (insert before it, insert after it, or delete it). Here
is the rough set of steps I used to solve the problem. These are the steps
that I would put in a design document.
- create a dllist and an inputstruct to read from stdin.
- create the code to implement the prepend command.
- create the code to traverse the list and print its contents. I could
now call this print function after executing each of my commands, and
specifically I could now check to see that prepend was
working properly.
- create the code to implement the append command. Check it
first using only append commands, then mix in prepend commands.
- create the code to test the delete command. This is the next
easiest command because it requires the least work. I traverse the list
to try to find the indicated photo and call dll_delete when I find the
node that contains it. I put the code for finding the target node in
a function called find_node, because I knew I would use it again
with the before and after commands. To test my
code I did a few prepends, then a few deletes, and then I mixed in
prepends, appends, and deletes.
- create the code to test the after command. I called the
find_node function that I wrote to support the delete
command, and then used dll_insert_after to insert after the
target node. First I tested the code with a single append, followed
by a bunch of after commands, then mixed in the previously implemented
commands.
- create the code to test the before command. I basically cut
and pasted the after code, but called dll_insert_before.
I tested the code with a single append, followed by a bunch of before
commands, then mixed in the previously implemented commands.
- I now figured out the possible error conditions and implemented code
to implement each one. I typically tested immediately after each
new error condition to make sure it worked, before going on to the next
one.
Notice that I did a great deal of incremental development and testing. If I
tried to write the whole program at once, it would have been much more difficult
to track down errors when they occurred.
Part 3 -- linenum
In the last lab you wrote a program called linenum
that takes a filename and an arbitrary number of words from the command
line and then prints the line numbers from the file
on which each of these words appears.
In this lab you will modify linenum so that for each unique word
in the file, it prints the line numbers on which that word appears.
You will need to print out the words in alphabetical order, and so you
will want to use dllists for this lab, rather than the sllists you used
in the previous lab.
Here are the program requirements:
- You must use the fields library to read each line of
input.
- Each line of output should
consist of a word and then the line numbers on which it appears.
- Line numbers should not be duplicated.
- The words should be printed in ascending alphabetical order.
For example, if the file ice_cream
contains the lines:
ice cream is nice
but chocolate ice
cream is dandy
then the output of the command
<UNIX> linenum ice_cream
will be
but 2
chocolate 2
cream 1 3
dandy 3
ice 1 2
is 1 3
nice 1
Format of the Output
Your output should be formatted as follows:
- The words should be left-justified in a 10 character wide field.
- There should be a single space between the word field and the first line number.
Extra spaces may appear because the word or line number do not fully occupy their
field.
- The line numbers should be right-justified in a 4 digit wide field.
Implementation Advice
Like the previous lab, you can still use lists to store the line numbers on
which a word appears. If you want, you can continue to use sllists for
storing line numbers, or you can convert them to dllists if you wish.
You can use the same trick of malloc'ing
space for an integer, storing the integer in that space,
and passing a pointer to that integer to the list's append
function. Unlike the previous lab you can no longer use an array to store
the words because 1) you do not know how many words are in the file, and
2) you will need to insert new words in alphabetical order, which means
that you will need to insert into the middle of your set of words. Hence you
will need to use a dllist to keep track of your words. For each word
you will need a struct that has two fields, one to store a pointer to
the word and one
to store a pointer to the line number dllist for that word.
For each word in
the file you should search the word list to see if the word is already on the
word list. If it is, then you will need to add the line number to the end of the
word's line number list, unless that line number is already on the
word's line number list. If the word is not
on the word list, then you will need to traverse the word list to find where the
word should appear in alphabetical order, and then insert the word into the
word list list. You should then add its line number as the first element of
the word's line
number list.
Design Document
Starting with this lab, we will be asking you to identify the error and
boundary conditions for which you should test
in your design document. Once the design documents have been collected, we
will tell you the list of errors and boundar conditions
we wish you to check for. You can use my
executables to determine the error messages that get printed for each type
of error.
Your design document for this lab
will consist of two parts, one for photo_sorter
and one for line_num.
Part 1 -- photo_sorter
For photo_sorter your design document should enumerate the list
of potential error and boundary conditions for which you should test.
Part 2 -- linenum
For line_num you should:
- draw a picture of what your data structures might look like for
the file ice_cream. It is okay to embed data like words
or line numbers in your nodes.
- enumerate the list of potential error conditions and boundary conditions
for which you should test.
- write down pseudo-code or a list of the high-level steps that your solution
will entail.
The design document that Thomas Hooper gave you for lab 4 is one version of a
high-level list of steps to perform.
What to Submit
You will submit your design document in the lab when the TA calls for it.
Submit the following source files:
- dllist.c
- photo_sorter.c
- linenum.c