С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 49
Текст из файла (страница 49)
Проверить,пусто ли оно, можно с помощью функции-члена size():294С++ для начинающихif ( text_map->size() )display_map_text( text_map );Но более простым способом, без подсчета элементов, будет вызов функции-членаif ( ! text_map->empty() )empty():display_map_text( text_map );6.12.4. СловарьВот небольшая программа, иллюстрирующая построение отображения, поиск в нем иобход элементов. Здесь используются два отображения.
Первое, необходимое дляпреобразования слов, содержит два элемента типа string. Ключом является слово,которое нуждается в специальной обработке, а значением – слово, заменяющее ключ. Дляпростоты мы задали пары ключ/значение непосредственно в тексте программы (выможете модифицировать программу так, чтобы она читала их из стандартного ввода илииз файла). Второе отображение используется для подсчета произведенных замен. Текстпрограммы выглядит следующим образом:295#include <map>#include <vector>#include <iostream>С++для начинающих#include<string>296int main(){map< string, string > trans_map;typedef map< string, string >::value_type valType;// первое упрощение:// жестко заданный словарьtrans_map.insert( va1Type(trans_map.insert( va1Type(trans_map.insert( va1Type(trans_map.insert( va1Type(trans_map.insert( va1Type(trans_map.insert( va1Type(trans_map.insert( va1Type(trans_map.insert( va1Type("gratz","'em","cuz","nah","sez","tanx","wuz","pos","grateful""them""because""no""says""thanks""was""suppose"));));));));));));));));// напечатаем словарьmap< string,string >::iterator it;cout << "Наш словарь подстановок: \n\n";for ( it = trans_map.begin();it != trans_map.end(); ++it )cout << "ключ: "<< (*it).first << "\t"<< "значение: " << ("it).second << "\n";cout << "\n\n";// второе упрощение: жестко заданный текстstring textarray[14]={ "nah", "I", "sez", "tanx","cuz", "I", "wuz", "pos", "to", "not","cuz", "I", "wuz", "gratz" };vector< string > text( textarray, textarray+14 );vector< string >::iterator iter;// напечатаем текстcout << "Исходный вектор строк:\n\n";int cnt = 1;for ( iter = text-begin(); iter != text.end();++iter,++cnt )cout << *iter << ( cnt % 8 ? " " : "\n" );cout << "\n\n\n";// map для сбора статистикиmap< string,int > stats;typedef map< string,int >::value_type statsValType;// здесь происходит реальная работаfor ( iter=text.begin(); iter != text.end(); ++iter )if (( it = trans_map.find( *iter ))!= trans_map.end() ){if ( stats.count( *iter ))stats [ *iter ] += 1;else stats.insert( statsVa1Type( *iter, 1 ));*iter = (*it).second;}// напечатаем преобразованный текстcout << "Преобразованный вектор строк:\n\n";cnt = 1;for ( iter = text.begin(); iter != text.end();++iter, ++cnt )cout << *iter << ( cnt % 8 ? " " : "\n" );cout << "\n\n\n";// напечатаем статистикуcout << "И напоследок статистика:\n\n";map<string,int,less<string>,allocator>::iterator siter;for (siter=stats.begin(); siter!=stats.end(); ++siter)cout << (*siter).first<< " "С++ для начинающих297}Вот результат работы программы:Наш словарь подстановок:key:key:key:key:key:key:key:key:'emcuzgratznahposseztanxwuzvalue:value:value:value:value:value:value:value:thembecausegratefulnosupposesaysthankswasИсходный вектор строк:nah I sez tanx cuz I wuz posto not cuz I wuz gratzПреобразованный вектор строк:no I says thanks because I was supposeto 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?С++ для начинающих298map<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 необходимо включить соответствующий заголовочныйфайл:С++ для начинающих#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() не вставляет элементы, она только присваивает299С++ для начинающихкаждому элементу новое значение.
Однако ассоциативные контейнеры не позволяют явнозадать размер. Чтобы скопировать элементы в наше множество, мы должны заставить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;использование множества, как показано в следующем фрагменте кода:}300С++ для начинающихКонтейнер 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Определите вектор, содержащий названия книг, которые вы собираетесь прочесть вближайшие шесть виртуальных месяцев, и множество, включающее названия ужепрочитанных произведений.