Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
86
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

301

Контейнер set не допускает дублирования ключей. Поэтому можно гарантировать, что occurrence_lines не содержит повторений. Теперь нам достаточно перебрать данное

register int size = occurrence_lines.size(); cout << "\n" << query_text

<<" встречается " << size

<<" раз(а):")

<<"\n\n";

set< short >::iterator it=occurrence_lines.begin(); for ( ; it != occurrence_lines.end(); ++it ) {

int line = -it;

cout << "\t( строка "

<<line + 1 << " ) "

<<(*text_file)[line] << endl;

множество, чтобы показать все номера строк, где встретилось данное слово:

}

(Полная реализация query_text() представлена в следующем разделе.)

Класс set поддерживает операции size(), empty() и erase() точно таким же образом, как и класс map, описанный выше. Кроме того, обобщенные алгоритмы предоставляют набор специфических функций для множеств, например set_union() (объединение) и set_difference() (разность). (Они использованы при реализации языка запросов в главе 17.)

Упражнение 6.23

Добавьте в программу множество слов, в которых заключающее 's' не подчиняется общим правилам и не должно удаляться. Примерами таких слов могут быть Pythagoras, Brahms и Burne_Jones. Включите в функцию suffix_s() из раздела 6.10 проверку этого набора.

Упражнение 6.24

Определите вектор, содержащий названия книг, которые вы собираетесь прочесть в ближайшие шесть виртуальных месяцев, и множество, включающее названия уже прочитанных произведений. Напишите программу, которая выбирает для вас книгу из вектора при условии, что вы ее еще не прочитали. Выбранное название программа должна заносить в множество прочитанных. Однако вы могли отложить книгу; следовательно, нужно обеспечить возможность удалять ее название из множества прочитанных. По окончании шести виртуальных месяцев распечатайте список прочитанного и непрочитанного.

6.14. Окончательная программа

Ниже представлен полный текст программы, разработанной в этой главе, с двумя модификациями: мы инкапсулировали все структуры данных и функции в класс TextQuery (в последующих главах мы обсудим подобное использование классов), кроме того, текст был изменен, так как наш компилятор поддерживал стандарт С++ не полностью.

С++ для начинающих

302

Например, библиотека iostream не соответствовала текущему стандарту. Шаблоны не поддерживали значения аргументов по умолчанию. Возможно, вам придется изменить кое-что в этой программе, чтобы она компилировалась в вашей системе.

#include <algorithm>

#include <string>

С++ для начинающих 303

#include <vector> #include <utility> #include <map> #include <set>

//заголовочный файл iostream, не отвечающий стандарту

#include <fstream.h>

//заголовочные файлы С

#include <stddef.h> #include <ctype.h>

// typedef для удобства чтения

location;

typedef pair<short,short>

typedef vector<location,allocator>

loc;

typedef vector<string,allocator>

text;

typedef pair<text*,loc*>

text_loc;

class TextQuery { public:

TextQuery() { memset( this, 0, sizeof( TextQuery )); }

static void

filter_elements( string felems ) { filt_elems = felems; }

void query_text();

void display_map_text();

void display_text_locations(); void doit() {

retrieve_text(); separate_words(); filter_text(); suffix_text(); strip_caps(); build_word_map();

}

private:

void

retrieve_text();

 

void

separate_words():

 

void

filter_text();

 

void

strip_caps();

 

void

suffix_textQ;

 

void

suffix_s( string& );

 

void build_word_map();

 

private:

 

*lines_of_text;

vector<string,allocator>

text_loc

*text_locations;

map< string,loc*,

*word_map;

less<string>,allocator>

static string

filt_elems;

};

 

 

string TextQuery::filt_elems( "\", •;: !?)(\V" );

int main()

{

TextQuery tq; tq.doit(); tq.query_text(); tq.display_map_text();

}

void TextQuery:: retrieve_text()

{

string file_name;

cout << "please enter file name: "; cin >> file_name;

ifstream infile( file_name.c_str(), ios::in );

С++ для начинающих

304

 

 

}

 

 

 

 

 

 

 

 

 

Упражнение 6.25

 

Объясните, почему нам потребовался специальный класс inserter

для заполнения

 

 

set<string> exclusion_set;

 

 

 

ifstream

infile( "exclusion_set" );

 

copy( default_excluded_words, default_excluded_words+25,

набора стоп-слов (это упоминается в разделе 6.13.1, а детально рассматривается в 12.4.1). inserter(exclusion_set, exclusion_set.begin() ));

Упражнение 6.26

Первоначальная реализация поисковой системы отражает процедурный подход: набор глобальных функций оперирует набором независимых структур данных. Окончательный вариант представляет собой альтернативный подход, когда мы инкапсулируем функции и данные в класс TextQuery. Сравните оба способа. Каковы недостатки и преимущества каждого?

Упражнение 6.27

В данной версии программы имя файла с текстом вводится по запросу. Более удобно было бы задавать его как параметр командной строки; в главе 7 мы покажем, как это делается. Какие еще параметры командной строки желательно реализовать?

6.15. Контейнеры multimap и multiset

Контейнеры map и set не допускают повторяющихся значений ключей, а multimap (мультиотображение) и multiset (мультимножество) позволяют сохранять ключи с дублирующимися значениями. Например, в телефонном справочнике может понадобиться отдельный список номеров для каждого абонента. В перечне книг одного автора может быть несколько названий, а в нашей программе с одним словом текста сопоставляется несколько позиций. Для использования multimap и multiset нужно

#include <map>

multimap< key_type, value_type > multimapName;

// ключ - string, значение - list< string > multimap< string, list< string > > synonyms;

#include <set>

включить соответствующий заголовочный файл map или set: multiset< type > multisetName;

Для прохода по мультиотображению или мультимножеству можно воспользоваться комбинацией итератора, который возвращает find() (он указывает на первый найденный элемент), и значения, которое возвращает count(). (Это работает, поскольку

в данных контейнерах элементы с одинаковыми ключами обязательно являются соседними). Например:

С++ для начинающих

305

#include <map> #include <string>

void code_fragment()

{

multimap< string, string > authors; string search_item( "Alain de Botton" ); // ...

int number = authors.count( search_item ); mu1timap< string,string >::iterator iter;

iter = authors.find( search_item );

for ( int cnt = 0; cnt < number; ++cnt, ++-iter ) do_something( *iter );

// ...

}

Более элегантный способ перебрать все значения с одинаковыми ключами использует специальную функцию-член equal_range(), которая возвращает пару итераторов. Один из них указывает на первое найденное значение, а второй на следующее за последним найденным. Если последний из найденных элементов является последним в контейнере, второй итератор содержит величину, равную end():

С++ для начинающих

306

#include <map> #include <string> #include <utility>

void code_fragment()

{

multimap< string, string > authors; // ...

string search_item( "Haruki Murakami" );

while ( cin && cin >> search_item ) switch ( authors.count( search_item ))

{

// не найдено case 0:

break;

// найден 1, обычный find() case 1: {

multimap< string, string >: iterator iter; iter = authors.find( search_item );

// обработка элемента ...

break;

}

// найдено несколько ...

default:

{

typedef multimap<string,string>::iterator iterator; pair< iterator, iterator > pos;

//pos.first - адрес 1-го найденного

//pos.second - адрес 1-го отличного

//от найденного

pos = authors.equa1_range( search_item ); for (; pos.first != pos.second; pos.first++ )

// обработка элемента ...

}

}

}

Вставка и удаление элементов в multimap и multiset ничем не отличаются от аналогичных операций с контейнерами map и set. Функция equal_range() доставляет

#include <multimap> #include <string>

typedef multimap< string, string >::iterator iterator; pair< iterator, iterator > pos;

string search_item( "Kazuo Ishiguro" );

//authors - multimap<string, string>

//эквивалентно

//authors.erase( search_item );

pos = authors.equa1_range( search_item );

итераторную пару, задающую диапазон удаляемых элементов: authors.erase( pos.first, pos.second );