Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chapter 7 Using Associative Containers. Objectives Introduce the concept of associative containers. Discuss the use of the map and pair structures. Introduce.

Similar presentations


Presentation on theme: "Chapter 7 Using Associative Containers. Objectives Introduce the concept of associative containers. Discuss the use of the map and pair structures. Introduce."— Presentation transcript:

1 Chapter 7 Using Associative Containers

2 Objectives Introduce the concept of associative containers. Discuss the use of the map and pair structures. Introduce the do – while repetition structure. Introduce recursion. Discuss the generation of random numbers.

3 C++ Don't include a single line in your code which you could not explain to your grandmother in a matter of two minutes. And of course... assume your grandmother is not Ada Lovelace. — Anonymous. PL/I and Ada started out with all the bloat, were very daunting languages, and got bad reputations (deservedly). C++ has shown that if you slowly bloat up a language over a period of years, people don't seem to mind as much. — James Hague.

4 Associative Containers All of the containers we have looked at so far ( vectors, lists, strings ) are sequential in that the elements are ordered. – The only way to find an element is by its position. It is hard to find a particular entry in a sequential container, unless you have already sorted the entries and you have random access. Now we introduce associative containers that automatically (and efficiently) arrange their elements by their values.

5 Associative Containers Each element will have a key which will be used to arrange the elements and which can also be used to search for an element. – Keeping track of students there would be several reasonable keys. Name ID number Email address etc. Each element will usually store other data as well. We often store data in key-data pairs where the key will be associated with the same collection of data until the pair is deleted. This is called an associative array.

6 maps The most common kind of associative array is called a map. Defined in. map operations are similar to vectors with the following exceptions. – The index does not need to be an integer, we can use any type that has an ordering ( string, float, etc.). – We cannot use algorithms that change the ordering of elements.

7 Counting Words A simple program to count the words in a string. int main() { string s; map counters; while (cin >> s) ++counters[s]; for (map ::const_iterator it = counters.begin(); it != counters.end(); ++it) { cout first second << endl; } return 0; }

8 Counting Words We must specify both the type of the keys and the values stored. map counters; This is “a map from string to int.” For every string there is an integer that is the counter for the number of times it occurs in the string. We increment these counters as if they were vector elements ++counters[s];

9 Creating Maps map m; – creates a new empty map with keys of type K and values of type V. map m(cmp); -- creates a new map that uses predicate cmp to order the elements.

10 Value-Initialization Notice, we operate as if there were an element for every single possible string, but this would be prohibitive. When we encounter a word for the first time the map creates a new entry. The new entry is value-initialized to 0.

11 Looping Over Map Elements To print out the counts from the map we loop over its elements. We initialize an iterator associated with the first element of the map. map ::const_iterator it = counters.begin(); We increment the iterator each time through the loop. ++it; The loop repeats while there are unvisited elements. it != counters.end(); All this is packaged in a for loop.

12 pairs Since we have an iterator associated with each entry, we need a way to get the key and the value. – We cannot just use the [] notation. We don’t know what word to use. We need to get that from the entry. cout first second << endl; A pair is a data structure that holds two elements. –first – contains the key. –second – contains the value. The elements of a map are pairs.

13 pairs To create a pair we need to indicate the types for both first and second. This is the type of a pair with keys of type K and values of type V. pair Note we cannot change key values without seriously messing up our map so the keys must be constant. We can dereference an iterator to elements of a map in two ways. (*it).first it->first

14 Cross-Reference Table Let’s write a program to generate a cross- reference table for a string. We will be able to indicate the where each word occurs. We read one line at a time. Each line will have a line number. We will split each line into words. – We will have this function be a parameter so it would be easy to change functions (say to find_urls )

15 Cross-Reference Table The words will be the keys and the associated values will be vectors of line numbers. The type of map will be map > Notice the space in > > is important because we need to distinguish this from the stream operator >>.

16 Cross-Reference Table map > xref(istream& in, vector find_words(const string&) = split) { string line; int line_number = 0; map > ret; while (getline(in, line)) { ++line_number; vector words = find_words(line); for (vector ::const_iterator it = words.begin(); it != words.end(); ++it) ret[*it].push_back(line_number); } return ret; }

17 Default Arguments Notice in the argument list the parameter vector find_words(const string&) = split –find_words is a parameter that must be passed a function that accepts a reference to a constant string and returns a vector of strings. The = split part is a default argument. The caller can omit this argument and split will be used by default. If an argument is provided in the call, it will be used instead.

18 Default Arguments This function can be called two ways. –xref(cin) – uses split to find words. –xref(cin, find_urls) – uses find_urls. Any default arguments must come at the end of the argument list.

19 Reference List We create a vector of the words in a line. Next, we loop over the words in the line. – An iterator it is created that marches across the vector of words For each word we add a reference. – Dereference the iterator to get the word *it – Get the map entry for this word. ret[*it] – Push the currently line number onto this word’s list of references. ret[*it].push_back(line_number);

20 Value-Initialization Revisited The first time we encounter a word, its vector of references will be created and initialized to be empty.

21 Main function int main() { map > ret = xref(cin); for (map >::const_iterator it = ret.begin(); it != ret.end(); ++it) { cout first << " occurs on line(s): ”; vector ::const_iterator line_it = it->second.begin(); cout << *line_it; ++line_it; while (line_it != it->second.end()) { cout << ", " << *line_it; ++line_it; } cout << endl; } return 0; }

22 Lists with Commas First, notice that if a words is the key of an entry in the map, we know it will have at least one line number in its list. Second, notice the trick in the output that prints commas in the list of line numbers if there is more than one entry in its vector. vector ::const_iterator line_it = it->second.begin(); cout << *line_it; ++line_it; while (line_it != it->second.end()) { cout << ", " << *line_it; ++line_it;

23 Generating Random Sentence We will define a sentence structure (a grammar). First define possible types of words. – = cat, dog, table – = jumps, sits – = large, brown, absurd

24 Generating Random Sentences Next, we will use these words to define phrases. – =, – = on the stairs, under the sky, wherever it wants Finally we put words and phrases together to construct sentences. – = the

25 Storing the Rules We have two kinds of entries. – Categories – enclosed in angle brackets. – Ordinary words. We need to be able to match each category to a rule that will expand that category. There may be more than one rule for each category. We want a map that uses the category as the key and stores the list of possible rules as the value. We can store the categories as strings. We will store each rule as a list ( vector ) of strings. Each category will have a list ( vector) of rules.

26 Storing the Rules Here are our types for storing the grammar. typedef vector Rule; typedef vector Rule_collection; typedef map Grammar;

27 Reading the Rules Now we are ready to read the rules. We will read a line and split it into words. – The first word will be the category (assume it has the angle brackets <>). – The remaining words will be a rule for this category. We will read one line for each rule. – A category may have more than one line of input one for each rule.

28 Reading the Rules This is a sample of the input. cat dog table large brown absurd jumps sits on the stairs under the sky whenever it wants the

29 Reading the Rules There is a lot of typing here so the example code on the web site is designed to read from the file “grammar.txt”. The example code in the book reads from the keyboard.

30 Reading the Rules Grammar read_grammar(istream& in) { Grammar ret; string line; while (getline(in, line)) { vector entry = split(line); if (!entry.empty()) ret[entry[0]].push_back( Rule(entry.begin() + 1, entry.end())); } return ret; }

31 Reading the Rules Notice that the first word on the line is used as the key for the map. ret[entry[0]] We then create a copy of the rest of the line as a vector of strings ( Rule ) and append it the vector of Rules for this category.

32 Generating Sentences We will generate a vector of strings. Each string will represents a word or a category in our sentence. We need to look through the vector and identify strings that represent categories (surrounded by angle brackets <>). We will replace these entries in the vector with one or more entries according to the rules of the grammar. We start with the string and work from there.

33 Generating Sentences vector gen_sentence(const Grammar& g) { vector ret; gen_aux(g, " ", ret); return ret; } This calls the function gen_aux to expand according to the grammar rules in g and append the result to the vector ret.

34 Generating Sentences We also need to be able to determine which strings represent categories. bool bracketed(const string& s) { return s.size() > 1 && s[0] == ' '; } This checks that the string is not empty, the first character is ‘ ’. Short circuiting is important here.

35 Generating Sentences Most of the action takes place in gen_aux. This function has the following parameters: – g – a grammar – word – a reference to a constant string. – ret – a reference to a vector of strings. The idea is to randomly select one of the grammar rules that can be used to replace word.

36 Generating Sentences The problem here is that when we expand a string, we may end up with categories that need to be expanded yet again. This sounds like a version of the original problem, so we will call gen_aux again for each category generated. Notice that this is a case of gen_aux calling itself.

37 Recursion When a function calls itself it is called recursion. Recursion can often be used to solve what seems like a complex problem with a short program. Recursion can also be confusing and must be used with care. You can end up in an endless loop very easily.

38 gen_aux void gen_aux(const Grammar& g, const string& word, vector & ret) { if (!bracketed(word)) { ret.push_back(word); } else { Grammar::const_iterator it = g.find(word); if (it == g.end()) throw logic_error("empty rule"); const Rule_collection& c = it->second; const Rule& r = c[nrand(c.size())]; for (Rule::const_iterator i = r.begin(); i != r.end(); ++i) gen_aux(g, *i, ret); } }

39 gen_aux First, the function checks to see if word is a category that needs to expanding. – If it is not then it is simply pushed onto the ret vector. To expand word we need to find an applicable rule in the grammar. – It is temping to use g[word]. – This is dangerous because if g[word] doesn’t exist this line will create it, producing an incorrect result. – This is likely to happen because it is hard to type a grammar in with no mistakes. We will use the find member of the map class to do it safely.

40 gen_aux The find method returns an iterator ( it ) that refers to the correct element if it exist and to g.end() if it does not. If we see g.end() there is a problem so we throw an exception. it->second is now the list of applicable rules which we will call c as a synonym.

41 gen_aux Next we use the nrand function to choose one of the rules at random. const Rule& r = c[nrand(c.size())] Notice that r is a constant reference to the Rule we want to use. Recall that a Rule is a vector of strings. We will discuss nrand in a moment. Finally we loop over the entries of our Rule r and recursively call gen_aux on each of them.

42 main int main() { srand(time(NULL)); ifstream grammerfile("grammar.txt"); vector sentence = gen_sentence(read_grammar(grammerfile)); grammerfile.close(); vector ::const_iterator it = sentence.begin(); if (!sentence.empty()) { cout << *it; ++it; } while (it != sentence.end()) { cout << " " << *it; ++it; } cout << endl; return 0; }

43 main We use the following line to seed the random number generator so that we don’t get the same string each time we run the program. srand(time(NULL)); Everything else is fairly straight forward. Notice the trick to print spaces between the words of the sentence.

44 Generating Random Numbers The function rand is defined in. This function returns a “random” integer in the range [0, RAND_MAX]. RAND_MAX is system dependent. We want to use this to generate a random number in the range [0,n). We partition [0, RAND_MAX] into n equal-sized buckets and see which bucket our number lands in.

45 Generating Random Numbers int nrand(int n) { if (n RAND_MAX) throw domain_error("Argument to nrand is out of range"); const int bucket_size = RAND_MAX / n; int r; do r = rand() / bucket_size; while (r >= n); return r; }

46 do – while Loop When partition our interval into buckets, things may not divide evenly. There may be some numbers not in any bucket. When we choose a random number it may not actually fall in a bucket. We want to keep choosing until we get a good result. To do this it is easiest to use a post-condition loop. – Check at the end to see if you need to do it again. – In C++ this structure is the do—while loop.

47 do – while Loop do statement while(condition); With a do – while loop the body will always be executed at least once. The condition is checked after the statement is executed. The body will continue to be repeatedly executed as long as the condition is true.

48 Homework Chapter 7 (page 138) Total 50 pts possible. – 7-0 – 7-1 (email, 10 pts) – 7-3 (email, 15 pts) – 7-8 (email, 15 pts) – Write a program that reads in Student_info records and stores them in a map that is ordered by name. Allow the user to request a specific record and then print it out. Note you may need to modify the Student_info type to separate out the key. (email, 25 pts)


Download ppt "Chapter 7 Using Associative Containers. Objectives Introduce the concept of associative containers. Discuss the use of the map and pair structures. Introduce."

Similar presentations


Ads by Google