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

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

  "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 <<
  "warning! unable to open word exclusion file! -- "
  << "using default set\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() ));
  }
 
  // пробежимся по всем словам, вставляя пары
  vector *text_words =
  text_locations->first;
  vector *text.locs =
  text_locations->second;
 
  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;
 
  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] );
  }
 }
 
 void
 TextQuery::
 query_text()
 {
  string query_text;
 
  do {
  cout
  << "enter a word against which to search the text.\n"
  << "to quit, enter a single character ==> ";
  cin >> query_text;
 
  if ( query_text.size() < 2 ) break;
 
  string caps( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" );
  string::size_type pos = 0;
  while (( pos = query_text.find_first_of( caps, pos ))
  != string::npos )
  query_text[ pos ] = to1ower( query_text[pos] );
 
  // query_text должно быть введено
 
  if ( !word_map->count( query_text )) {
  cout << "\nSorry. There are no entries for "
  << query_text << ".\n\n";
  continue;
  }
 
  loc *ploc = (*word_map) [ query_text ];
 
  set,allocator> occurrence_1i nes;
  loc::iterator liter = ploc->begin(),
  liter_end = ploc->end();
 
  while ( liter != liter_end ) {
  occurrence_lines.1nsert(
  occurrence_lines.end(), (*liter).first);
  ++liter;
  }
 
  register int size = occurrence_lines.size();
  cout << "\n" << query_text
  << " occurs " << size
  << (size == 1 ? " time:" : " times:")
  << "\n\n";
 
  set,allocator>::iterator
  it=occurrence_lines.begin();
  for ( ; it != occurrence_"lines.end(); ++it ) {
  int line = *it;
 
  cout << "\t( line "
  // будем нумеровать строки с 1,
  // как это принято везде
  << line + 1 << " ) "
  << (*lines_of_text)[line] << endl;
  }
 
  cout << endl;
  }
  while ( ! query_text.empty() );
  cout << "Ok, bye!\n";
 }
 
 void
 TextQuery::
 display_map_text()
 {
  typedef map, allocator> map_text;
  map_text::iterator iter = word_map->begin(),
  iter_end = word_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;
 }
 
 void
 TextQuery::
 disp1ay_text_locations()
 {
  vector *text_words =
  text_locations->first;
  vector *text_locs =
  text_locations->second;
 
  register int elem_cnt = text_words->size();
 
  if ( elem_cnt != text_locs->size() )
  {
  cerr
  << "oops! internal error: word and position vectors "
  << "are of unequal size\n"
  << "words: " << elem_cnt << " "
  << "locs: " << text_locs->size()
  << " -- bailing out!\n";
  exit( -2 );
  }
 
  for ( int ix=0; ix < elem_cnt; ix++ )
  {
  cout << "word: " << (*text_words)[ ix ] << "\t"
  << "location: ("
  << (*text_locs)[ix].first << ","
  << (*text.locs)[ix].second << ")"
  << "\n";
  }
  cout << endl;
 }
 Упражнение 6.25
 Объясните, почему нам потребовался специальный класс inserter для заполнения набора стоп-слов (это упоминается в разделе 6.13.1, а детально рассматривается в 12.4.1).
 set exclusion_set;
 ifstream infile( "exclusion_set" );
 
 copy( default_excluded_words, default_excluded_words+25,
  inserter(exclusion_set, exclusion_set.begin() ));
 Упражнение 6.26
 Первоначальная реализация поисковой системы отражает процедурный подход: набор глобальных функций оперирует набором независимых структур данных. Окончательный вариант представляет собой альтернативный подход, когда мы инкапсулируем функции и данные в класс TextQuery. Сравните оба способа. Каковы недостатки и преимущества каждого?
 Упражнение 6.27
 В данной версии программы имя файла с текстом вводится по запросу. Более удобно было бы задавать его как параметр командной строки; в главе 7 мы покажем, как это делается. Какие еще параметры командной строки желательно реализовать?
 6.15. Контейнеры multimap и multiset
 Контейнеры map и set не допускают повторяющихся значений ключей, а multimap (мультиотображение) и multiset (мультимножество) позволяют сохранять ключи с дублирующимися значениями. Например, в телефонном справочнике может понадобиться отдельный список номеров для каждого абонента. В перечне книг одного автора может быть несколько названий, а в нашей программе с одним словом текста сопоставляется несколько позиций. Для использования multimap и multiset нужно включить соответствующий заголовочный файл – map или set:
 #include
 multimap< key_type, value_type > multimapName;
 
 // ключ - string, значение - list< string >
 multimap< string, list< string > > synonyms;
 
 #include
 multiset< type > multisetName;
 Для прохода по мультиотображению или мультимножеству можно воспользоваться комбинацией итератора, который возвращает find() (он указывает на первый найденный элемент), и значения, которое возвращает count(). (Это работает, поскольку в данных контейнерах элементы с одинаковыми ключами обязательно являются соседними). Например:
 #include
 #include
 
 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():
 #include
 #include
 #include
 
 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::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
 #include
 
 typedef multimap< string, string >::iterator iterator;
 pair< iterator, iterator > pos;
 string search_item( "Kazuo Ishiguro" );
 
 // authors - multimap
 // эквивалентно
 // authors.erase( search_item );
 pos = authors.equa1_range( search_item );
 authors.erase( pos.first, pos.second );
 При каждом вызове функции-члена insert() добавляется новый элемент, даже если в контейнере уже был элемент с таким же ключом. Например:
 typedef multimap::value_type valType;
 multimap authors;
 
 // первый элемент с ключом Barth
 authors.insert( valType (
  string( "Barth, John" ),
  string( "Sot-Weed Factor" )));
 
 // второй элемент с ключом Barth
 authors.insert( va1Type(
  string( "Barth, John" ),
  string( "Lost in the Funhouse" )));
 Контейнер multimap не поддерживает операцию взятия индекса. Поэтому следующее выражение ошибочно:
 authors[ "Barth, John" ]; // ошибка: multimap
 Упражнение 6.28
 Перепишите программу текстового поиска из раздела 6.14 с использованием multimap для хранения позиций слов. Каковы производительность и дизайн в обоих случаях? Какое решение вам больше нравится? Почему?
 6.16. Стек
 В разделе 4.5 операции инкремента и декремента были проиллюстрированы на примере реализации абстракции стека. В общем случае стек является очень полезным механизмом для сохранения текущего состояния, если в разные моменты выполнения программы одновременно существует несколько состояний, вложенных друг в друга. Поскольку стек – это важная абстракция данных, в стандартной библиотеке С++ предусмотрен класс stack, для использования которого нужно включить заголовочный файл:
 #include
 В стандартной библиотеке стек реализован несколько иначе, чем у нас. Разница состоит в том, что доступ к элементу с вершины стека и удаление его осуществляются двумя функциями – top() и pop(). Полный набор операций со стеком приведен в таблице 6.5.
 Таблица 6.5. Операции со стеком
 
 Операция Действие
 empty() Возвращает true, если стек пуст, и false в противном случае
 size() Возвращает количество элементов в стеке
 pop() Удаляет элемент с вершины стека, но не возвращает его значения
 top() Возвращает значение элемента с вершины стека, но не удаляет его
 push(item) Помещает новый элемент в стек
 
 В нашей программе приводятся примеры использования этих операций:

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

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