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

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

297

}

Вот результат работы программы:

Наш словарь подстановок:

key: 'em

value: them

key: cuz

value: because

key: gratz

value: grateful

key: nah

value: no

key: pos

value: suppose

key: sez

value: says

key: tanx

value: thanks

key: wuz

value: was

Исходный вектор строк:

nah I sez tanx cuz I wuz pos to not cuz I wuz gratz

Преобразованный вектор строк:

no I says thanks because I was suppose to not because I was grateful

И напоследок статистика:

cuz было заменено 2 раз(а) gratz было заменено 1 раз(а) nah было заменено 1 раз(а) pos было заменено 1 раз(а) sez было заменено 1 раз(а) tanx было заменено 1 раз(а) wuz было заменено 2 раз(а)

6.12.5. Удаление элементов map

Существуют три формы функции-члена erase() для удаления элементов отображения.

Для единственного элемента используется erase() с ключом или итератором в качестве аргумента, а для последовательности эта функция вызывается с двумя итераторами.

string removal_word;

cout << "введите удаляемое слово: "; cin >> removal_word;

if ( text_map->erase( remova1_word ))

cout << "ok: " << remova1_word << " удалено\n";

Например, мы могли бы позволить удалять элементы из text_map таким образом: else cout << "увы: " << remova1_word << " не найдено!\n";

Альтернативой является проверка: действительно ли слово содержится в text_map?

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

298

 

 

map<string,loc*>::iterator where;

 

 

 

 

 

 

where = text_map.find( remova1_word );

 

 

 

if ( where == text_map->end() )

 

 

 

cout << "увы: " << remova1_word << " не найдено!\n";

 

 

 

else {

 

 

 

text_map->erase( where );

 

 

 

cout << "ok: " << remova1_word << " удалено!\n";

 

 

 

}

 

 

 

 

 

 

 

В нашей реализации text_map с каждым словом сопоставляется множество позиций, что

 

несколько усложняет их хранение и извлечение. Вместо этого можно было бы иметь по

 

одной позиции на слово. Но контейнер map не допускает дублирующиеся ключи. Нам

 

следовало бы воспользоваться классом multimap, который рассматривается в разделе

 

6.15.

 

Упражнение 6.20

 

Определите отображение, где ключом является фамилия, а значением вектор с именами

 

детей. Поместите туда как минимум шесть элементов. Реализуйте возможность делать

 

запрос по фамилии, добавлять имена и распечатывать содержимое.

 

Упражнение 6.21

 

Измените программу из предыдущего упражнения так, чтобы вместе с именем ребенка

 

записывалась дата его рождения: пусть вектор-значение хранит пары строк имя и дата.

 

Упражнение 6.22

 

Приведите хотя бы три примера, в которых нужно использовать отображение. Напишите

 

определение объекта map для каждого примера и укажите наиболее вероятный способ

 

вставки и извлечения элементов.

 

6.13. Построение набора стоп-слов

Отображение состоит из пар ключ/значение. Множество (set), напротив, содержит неупорядоченную совокупность ключей. Например, бизнесмен может составить черный списокbad_checks, содержащий имена лиц, в течение последних двух лет присылавших фальшивые чеки. Множество полезно тогда, когда нужно узнать, содержится ли определенное значение в списке. Скажем, наш бизнесмен, принимая чек от кого-либо, может проверить, есть ли его имя в bad_checks.

Для нашей поисковой системы мы построим набор стоп-слов слов, имеющих семантически нейтральное значение (артикли, союзы, предлоги), таких, как the, and, into, with, but и т.д. (это улучшает качество системы, однако мы уже не сможем найти первое предложение из знаменитого монолога Гамлета: “To be or not to be?”). Прежде чем добавлять слово к word_map, проверим, не содержится ли оно в списке стоп-слов. Если содержится, проигнорируем его.

6.13.1. Определение объекта set и заполнение его элементами

Перед использованием класса set необходимо включить соответствующий заголовочный файл:

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

299

#include <set>

Вот определение нашего множества стоп-слов:

set<string> exclusion_set;

exclusion_set.insert( "the" );

Отдельные элементы могут добавляться туда с помощью операции insert(). Например: exclusion_set.insert( "and" );

Передавая insert() пару итераторов, можно добавить целый диапазон элементов. Скажем, наша поисковая система позволяет указать файл со стоп-словами. Если такой

typedef set< string >::difference_type diff_type; set< string > exclusion_set;

ifstream infile( "exclusion_set" ); if ( ! infile )

{

static string default_excluded_words[25] = { "the","and","but","that","then","are","been", "can"."can't","cannot","could","did","for", "had","have","him","his","her","its","into", "were","which","when","with","would"

};

cerr << "предупреждение! невозможно открыть файл стоп-слов! -- " << "используется стандартный набор слов \n";

copy( default_excluded_words, default_excluded_words+25, inserter( exclusion_set, exclusion_set.begin() ));

}

else {

istream_iterator<string,diff_type> input_set(infile),eos; copy( input_set, eos, inserter( exclusion_set,

exclusion_set.begin() ));

файл не задан, берется некоторый набор слов по умолчанию:

}

В этом фрагменте кода встречаются два элемента, которые мы до сих пор не рассматривали: тип difference_type и класс inserter. difference_type это тип результата вычитания двух итераторов для нашего множества строк. Он передается в качестве одного из параметров шаблона istream_iterator.

copy() один из обобщенных алгоритмов. (Мы рассмотрим их в главе 12 и в Приложении.) Первые два параметра пара итераторов или указателей задают диапазон. Третий параметр является либо итератором, либо указателем на начало контейнера, в который элементы копируются.

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

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

300

каждому элементу новое значение. Однако ассоциативные контейнеры не позволяют явно задать размер. Чтобы скопировать элементы в наше множество, мы должны заставить copy() вставлять элементы. Именно для этого служит класс inserter (детально он рассматривается в разделе 12.4).

6.13.2. Поиск элемента

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

if ( exclusion_set.count( textword )) continue;

существование в функцию build_word_map():

// добавим отсутствующее слово

6.13.3. Навигация по множеству

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

tomorrow and tomorrow and tomorrow

однако такая строка будет представлена только один раз.

Одним из способов не учитывать повторное вхождение слова в строку является

//получим указатель на вектор позиций loc ploc = (*text_map)[ query_text ];

//переберем все позиции

//вставим все номера строк в множество set< short > occurrence_lines; loc::iterator liter = ploc->begin(),

liter_end = ploc->end();

while ( liter != liter_end ) { occurrence_lines.insert( occurrence_lines.end(),

(*liter).first ); ++liter;

использование множества, как показано в следующем фрагменте кода:

}