Previous fileTop of DocumentContentsIndex pageNext file
Apache C++ Standard Library User's Guide

9.3 Example Programs

In this section, we present three example programs that illustrate the use of maps and multimaps. These examples deal with a telephone database, graphs, and a concordance.

9.3.1 Example: A Telephone Database


NOTE -- The complete example program is in the file tutorial tele.cpp.

A maintenance program for a simple telephone database is a good application for a map. The database is simply an indexed structure, where the name of the person or business (a std::string) is the key value, and the telephone number (a long) is the associated entry. We might write such a class as follows:

Simple operations on our database are directly implemented using member functions of the map. Adding an element to the database is simply an insert(), removing an element is an erase(), and updating is a combination of the two. To print all the entries in the database we can use the for_each() algorithm, and apply the following simple utility routine to each entry:

We now use a pair of slightly more complex operations to illustrate how a few of the algorithms described in Chapter 13 can be used with maps. Suppose we want to display all the phone numbers with a certain three-digit initial prefix. We use the std::find_if() function, which is not to be confused with the find() member function of map, to locate the first entry. Starting from this location, subsequent calls on std::find_if() uncover each successive entry:

For the predicate to this operation, we require a boolean function that takes only a single argument, the pair representing a database entry, and tells us whether or not it is in the given prefix. There is no obvious candidate function, and in any case the test prefix is not being passed as an argument to the comparison function. The solution to this problem is to employ a common C++ Standard Library technique: define the predicate function as an instance of a class, and store the test predicate as an instance variable in the class, initialized when the class is constructed. The desired function is defined as the function call operator for the class:

Our final example displays the directory sorted by prefix. It is not possible to alter the order of the maps themselves. Instead, we create a new map with the element types reversed and copy the values into the new map, which orders the values by prefix. Once the new map is created, it is printed:

Here is the function used to print the sorted entries:

9.3.2 An Example: Graphs


NOTE -- The complete version of this program is in the file graph.cpp.

A map whose elements are themselves maps is a natural representation for a directed graph. For example, suppose we use strings to encode the names of cities, and we wish to construct a map where the value associated with an edge is the distance between two connected cities. We could create such a graph as follows:

The type stringVector is a map of integers indexed by text strings. The type graph is, in effect, a two-dimensional sparse array, indexed by strings and holding int values. A sequence of assignment statements initializes the graph.

A number of classic algorithms can be used to manipulate graphs represented in this form. One example is Dijkstra's shortest-path algorithm. Dijkstra's algorithm begins from a specific city given as an initial location. A priority_queue of distance/city pairs is then constructed, and initialized with the distance from the starting city to itself (namely, zero). The definition for the distance pair datatype is as follows:

In the algorithm that follows, note how the conditional test is reversed on the priority_queue, because at each step we wish to pull the smallest, and not the largest, value from the collection. On each iteration around the loop we pull a city from the queue. If we have not yet found a shorter path to the city, the current distance is recorded, and we can compute the distance from this city to each of its adjacent cities by examining the graph. This process continues until the priority_queue becomes exhausted:

Notice that this relatively simple algorithm makes use of vector (Chapter 5), map (Chapter 9), string (Chapter 12), and priority_queue (Chapter 11).

9.3.3 Example: A Concordance

A concordance is an alphabetical listing of words in a text that shows the line numbers on which each word occurs.


NOTE -- The executable version of this program is in the file concord.cpp.

We develop a concordance to illustrate the use of the map and multimap container classes. The data values are maintained in the concordance by a multimap, indexed by word (a string) and holding the line numbers (int). A multimap is used because the same word is expected to appear on different lines; indeed, discovering such connections is one of the primary purposes of a concordance. Another possibility would be to use a map and use a set of integer elements as the associated values.

The creation of the concordance is divided into two steps: the program generates the concordance by reading lines from an input stream, then prints the result on the output stream. This is reflected in the two member functions readText() and printConcordance(). The first of these, readText(), is written as follows:

Lines are read from the input stream one by one. The text of the line is first converted into lower case, then the line is split into words using the function split() described in Section 12.3. Each word is then entered into the concordance. The method used to enter a value into the concordance is as follows:

The major portion of addWord() is concerned with ensuring that values are not duplicated in the word map if the same word occurs twice on the same line. To assure this, the range of values matching the key is examined, each value is tested, and if any match the line number then no insertion is performed. It is only if the loop terminates without discovering the line number that the new word/line number pair is inserted.

The final step is to print the concordance. This is performed in the following fashion:

An iterator loop is used to cycle over the elements being maintained by the word list. Each new word generates a new line of output; thereafter, line numbers appear separated by spaces. For example, if the input was the text:

The output, from best to worst, would be:

best:

1

it:

1 2

of:

1 2

the:

1 2

times:

1 2

was:

1 2

worst:

2



Previous fileTop of DocumentContentsIndex pageNext file