<< Пред.           стр. 28 (из 121)           След. >>

Список литературы по разделу

 Теперь нам надо синхронно обойти оба вектора, учитывая два случая:
 слово встретилось впервые. Нужно поместить в map новую пару ключ/значение;
 слово встречается повторно. Нам нужно обновить вектор позиций, добавив дополнительную пару (номер строки, номер колонки).
 Вот текст функции:
 register int elem_cnt = text_words->size();
 for ( int ix=0; ix < elem_cnt; ++ix )
 {
  string textword = ( *text_words )[ ix ];
 
  // игнорируем слова короче трех букв
  // или присутствующие в списке стоп-слов
  if ( textword.size() < 3 ||
  exclusion_set.count( textword ))
  continue;
 
  // определяем, занесено ли слово в отображение
  // если count() возвращает 0 - нет: добавим его
  if ( ! word_map->count((*text_words)[-ix] ))
  {
  loc *ploc = new vector;
  ploc->push_back( (*text_locs) [ix] );
  word_map->insert(value_type((*text_words)[ix],ploc));
  }
  else
  // добавим дополнительные координаты
  (*word_map)[(*text_words)[ix]]->
  push_back((*text_locs)[ix]);
 }
 Синтаксически сложное выражение
 (*word_map)[(*text_words)[ix]]->
  push_back((*text_locs)[ix]);
 будет проще понять, если мы разложим его на составляющие:
 // возьмем слово, которое надо обновить
 string word = (*text_words) [ix];
 
 // возьмем значение из вектора позиций
 vector *ploc = (*word_map) [ word ];
 
 // возьмем позицию - пару координат
 loc = (*text_locs)[ix];
 
 // вставим новую позицию
 ploc->push_back(loc);
 Выражение все еще остается сложным, так как наши векторы представлены указателями. Поэтому вместо употребления оператора взятия индекса:
 string word = text_words[ix]; // ошибка
 мы вынуждены сначала разыменовать указатель на вектор:
 string word = (*text_words) [ix]; // правильно
 В конце концов build_word_map() возвращает построенное отображение:
 return word_map;
 Вот как выглядит вызов этой функции из main():
 int main()
 {
  // считываем файл и выделяем слова
  vector *text_file = retrieve_text();
  text_loc *text_locations = separate_words( text_file );
 
  // обработаем слова
  // ...
 
  // построим отображение слов на векторы позиций
  map,allocator>
  *text_map = build_word_map( text_locatons );
 
  // ...
 }
 6.12.2. Поиск и извлечение элемента отображения
 Оператор взятия индекса является простейшим способом извлечения элемента. Например:
 // map word_count;
 int count = word_count[ "wrinkles" ];
 Однако этот способ работает так, как надо, только при условии, что запрашиваемый ключ действительно содержится в отображении. Иначе оператор взятия индекса поместит в отображение элемент с таким ключом. В данном случае в word_count занесется пара
 
 string( "wrinkles" ), 0
 
 Класс map предоставляет две операции для того, чтобы выяснить, содержится ли в нем определенное значение ключа.
 count(keyValue): функция-член count() возвращает количество элементов с данным ключом. (Для отображения оно равно только 0 или 1). Если count() вернула 1, мы можем смело использовать индексацию:
 int count = 0;
 if ( word_count.count( "wrinkles" ))
  count = word_count[ "wrinkles" ];
 find(keyValue): функция-член find() возвращает итератор, указывающий на элемент, если ключ найден, и итератор end() в противном случае. Например:
 int count = 0;
 map::iterator it = word_count.find( "wrinkles" );
 if ( it != word_count.end() )
  count = (*it).second;
 Значением итератора является указатель на объект pair, в котором first содержит ключ, а second – значение. (Мы вернемся к этому в следующем подразделе.)
 6.12.3. Навигация по элементам отображения
 После того как мы построили отображение, хотелось бы распечатать его содержимое. Мы можем сделать это, используя итератор, начальное и конечное значение которого получают с помощью функций-членов begin() и end(). Вот текст функции display_map_text():
 void
 display_map_text( map *text_map )
 {
  typedef map tmap;
  tmap::iterator iter = text_map->begin(),
  iter_end = text_map->end();
 
  while ( iter != iter_end )
  {
  cout << "word: " << (*iter).first << " (";
  int loc_cnt = 0;
  loc *text_locs = (*iter).second;
  loc::iterator liter = text_locs->begin(),
  liter_end = text_locs->end();
 
  while (liter != liter_end ) {
  if ( loc_cnt )
  cout << ',';
  else ++loc_cnt;
 
  cout << '(' << (*liter).first
  << ',' << (*liter).second << ')';
 
  ++liter;
  }
 
  cout << ")\n";
  ++iter;
  }
  cout << endl;
 }
 Если наше отображение не содержит элементов, данная функция не нужна. Проверить, пусто ли оно, можно с помощью функции-члена size():
 if ( text_map->size() )
  display_map_text( text_map );
 Но более простым способом, без подсчета элементов, будет вызов функции-члена empty():
 if ( ! text_map->empty() )
  display_map_text( text_map );
 
 6.12.4. Словарь
 Вот небольшая программа, иллюстрирующая построение отображения, поиск в нем и обход элементов. Здесь используются два отображения. Первое, необходимое для преобразования слов, содержит два элемента типа string. Ключом является слово, которое нуждается в специальной обработке, а значением – слово, заменяющее ключ. Для простоты мы задали пары ключ/значение непосредственно в тексте программы (вы можете модифицировать программу так, чтобы она читала их из стандартного ввода или из файла). Второе отображение используется для подсчета произведенных замен. Текст программы выглядит следующим образом:
 #include
 #include
 #include
 #include
 
 int main()
 {
  map< string, string > trans_map;
  typedef map< string, string >::value_type valType;
 
  // первое упрощение:
  // жестко заданный словарь
  trans_map.insert( va1Type( "gratz", "grateful" ));
  trans_map.insert( va1Type( "'em", "them" ));
  trans_map.insert( va1Type( "cuz", "because" ));
  trans_map.insert( va1Type( "nah", "no" ));
  trans_map.insert( va1Type( "sez", "says" ));
  trans_map.insert( va1Type( "tanx", "thanks" ));
  trans_map.insert( va1Type( "wuz", "was" ));
  trans_map.insert( va1Type( "pos", "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,allocator>::iterator siter;
 
  for (siter=stats.begin(); siter!=stats.end(); ++siter)
  cout << (*siter).first << " "
  << "было заменено "
  << (*siter).second
  << (" раз(а)\n" );
 }
 Вот результат работы программы:
 
 Наш словарь подстановок:
 
 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() с ключом или итератором в качестве аргумента, а для последовательности эта функция вызывается с двумя итераторами. Например, мы могли бы позволить удалять элементы из text_map таким образом:
 string removal_word;
 cout << "введите удаляемое слово: ";
 cin >> removal_word;
 
 if ( text_map->erase( remova1_word ))
  cout << "ok: " << remova1_word << " удалено\n";
 else cout << "увы: " << remova1_word << " не найдено!\n";
 Альтернативой является проверка: действительно ли слово содержится в text_map?
 map::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 exclusion_set;
 Отдельные элементы могут добавляться туда с помощью операции insert(). Например:
 exclusion_set.insert( "the" );
 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 input_set(infile),eos;
  copy( input_set, eos, inserter( exclusion_set,

<< Пред.           стр. 28 (из 121)           След. >>

Список литературы по разделу