Язык программирования C++. Вводный курс

Соберем все вместе


Функция main() для нашего приложения текстового поиска выглядит следующим образом:

#include "TextQuery.h"

int main()

{

   TextQuery tq;

   tq.build_up_text();

   tq.query_text();

}



Функция-член build_text_map() – это не что иное, как переименованная функция doit() из раздела 6.14:

inline void

TextQuery::

build_text_map()

{

   retrieve_text();

   separate_words();

   filter_text();

   suffix_text();

   strip_caps();

   build_word_map();

}

Функция-член query_text()

заменяет одноименную функцию из раздела 6.14. В первоначальной реализации в ее обязанности входили прием запроса от пользователя и вывод ответа. Мы решили сохранить за query_text() эти задачи, но реализовать ее по-другому[19]:

void

TextQuery::query_text()

{

    /* локальные объекты:

     *

     * text: содержит все слова запроса

     * query_text: вектор для хранения пользовательского запроса

     * caps: фильтр для поддержки преобразования

     * прописных букв в строчные

     *

     * user_query: объект UserQuery, в котором инкапсулировано

     *             собственно вычисление ответа на запрос

     */

           string text;

           string caps( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" );

           vector<string, allocator> query_text;

           UserQuery user_query;

     // инициализировать статические члены UserQuery

           NotQuery::all_locs( text_locations->second );

           AndQuery::max_col( &line_cnt );

           UserQuery::word_map( word_map );

           do {

           // удалить предыдущий запрос, если он был

                  query_text.clear();

           cout << "Введите запрос. Пожалуйста, разделяйте все его "

                        << "элементы пробелами.\n"

                << "Запрос (или весь сеанс) завершается точкой ( . ).\n\n"

                         << "==> ";

          

          /*

           * прочитать запрос из стандартного ввода,

           * преобразовать все заглавные буквы, после чего

           * упаковать его в query_text ...

           *

           * примечание: здесь производятся все действия по

           * обработке запроса, связанные собственно с текстом ...

           */

                  while( cin  >> text )

                  {

                       if ( text == "." )

                            break;

                       string::size_type pos = 0;

                while (( pos = text.find_first_of( caps, pos ))

                                     != string::npos )

                          text[pos] = tolower( text[pos] );

                       query_text.push_back( text );

                  }

           // теперь у нас есть внутреннее представление запроса

           // обработаем его ...

                  if ( ! query_text.empty() )

                  {

                 // передать запрос объекту UserQuery

                         user_query.query( &query_text );

                 // вычислить ответ на запрос

                 // вернуть иерархию Query*

                 // подробности см. в разделе 17.7

                 // query - это член класса TextQuery типа Query*

                         query = user_query.eval_query();

                 // вычислить иерархию Query,

                 // реализация описана в разделе 17.7

                         query->eval();

                 // вывести ответ с помощью

                 // функции-члена класса TextQuery

                         display_solution();

                 // вывести на терминал пользователя дополнительную

                 // пустую строку

                         cout << endl;

                  }

        }

        while ( ! query_text.empty() );

        cout << "До свидания!\n";

<
}

Тестируя программу, мы применили ее к нескольким текстам. Первым стал короткий рассказ Германа Мелвилла “Bartleby”. Здесь иллюстрируется составной запрос AndQuery, для которого подходящие слова расположены в соседних строках. (Отметим, что слова, заключенные между символами косой черты, предполагаются набранными курсивом.)

Введите запрос. Пожалуйста, разделяйте все его элементы пробелами.

Запрос (или весь сеанс) завершается точкой ( . ).

==> John && Jacob && Astor

         john ( 3 ) lines match

         jacob ( 3 ) lines match

         john && jacob ( 3 ) lines match

         astor ( 3 ) lines match

         john && jacob && astor ( 5 ) lines match

Requested query: john && jacob && astor

( 34 ) All who know me consider me an eminently /safe/ man. The late

John Jacob

( 35 ) Astor, a personage little given to poethic enthusiasm, had no

hesitation in

( 38 ) my profession by the late John Jacob Astor, a name which, I admit

I love to

( 40 ) bullion. I will freely add that I was not insensible to the late

John Jacob

( 41 ) Astor's good opinion.

Следующий запрос, в котором тестируются скобки и составные операторы, обращен к тексту новеллы “Heart of Darkness” Джозефа Конрада:

==> horror || ( absurd && mystery ) || ( North && Pole )

         horror ( 5 ) lines match

         absurd ( 8 ) lines match

         mystery ( 12 ) lines match

         ( absurd && mystery ) ( 1 ) lines match

         horror || ( absurd && mystery ) ( 6 ) lines match

         north ( 2 ) lines match

         pole ( 7 ) lines match

         ( north && pole ) ( 1 ) lines match

         horror || ( absurd && mystery ) || ( north && pole )

                ( 7 ) lines match

Requested query: horror || ( absurd && mystery ) || ( north && pole )

( 257 ) up I will go there.' The North Pole was one of these

( 952 ) horros. The heavy pole had skinned his poor nose



( 3055 ) some lightless region of subtle horrors, where pure,

( 3673 ) " 'The horror! The horror!'

( 3913 ) the whispered cry, 'The horror! The horror! '

( 3957 ) absurd mysteries not fit for a human being to behold.

( 4088 ) wind. 'The horror! The horror!'

Последний запрос был обращен к отрывку из романа Генри Джеймса “Portrait of a Lady”. В нем иллюстрируется составной запрос в применении к большому текстовому файлу:

==> clever && trick || devious

         clever ( 46 ) lines match

         trick ( 12 ) lines match

         clever && trick ( 2 ) lines match

         devious ( 1 ) lines match

         clever && trick || devious ( 3 ) lines match

Requested query: clever && trick || devious

( 13914 ) clever trick she had guessed. Isabel, as she herself grew older

( 13935 ) lost the desire to know this lady's clever trick. If she had

( 14974 ) desultory, so devious, so much the reverse of processional.

There were

Упражнение 17.23

Реализованная нами обработка запроса пользователя обладает одним недостатком: она не применяет к каждому слову те же предварительные фильтры, что и программа, строящая вектор позиций (см. разделы 6.9 и 6.10). Например, пользователь, который хочет найти слово “maps”, обнаружит, что в нашем представлении текста распознается только “map”, поскольку существительные во множественном числе приводятся к форме в единственном числе. Модифицируйте функцию query_text() так, чтобы она применяла эквивалентные фильтры к словам запроса.

Упражнение 17.24

Поисковую систему можно было бы усовершенствовать, добавив еще одну разновидность запроса “И”, которую мы назовем InclusiveAndQuery и будем обозначать символом &. Строка текста удовлетворяет условиям запроса, если в ней находятся оба указанных слова, пусть даже не рядом. Например, строка

We were her pride of ten, she named us

удовлетворяет запросу:

pride & ten

но не:

pride && ten



Поддержите запрос InclusiveAndQuery.

Упражнение 17.25

Представленная ниже реализация функции display_solution()

может выводить только в стандартный вывод. Более правильно было бы позволить пользователю самому задавать поток ostream, в который надо направить вывод. Модифицируйте display_solution()

так, чтобы ostream

можно было задавать. Какие еще изменения необходимо внести в определение класса UserQuery?



void TextQuery::

display_solution()

{

           cout << "\n"

                << "Requested query: "

                << *query << "\n\n";

           const set<short,less<short>,allocator> *solution = query->solution();

          

           if ( ! solution->size() ) {

                cout << "\n\t"

                    << "Sorry, no matching lines were found in text.\n"

                    << endl;

           }

           set<short>::const_iterator

                  it = solution->begin(),

                  end_it = solution->end();

           for ( ; it != end_it; ++it ) {

                  int line = *it;

                  // пронумеруем строки с 1 ...

                  cout << "( " << line+1 << " ) "

                       << (*lines_of_text)[line] << '\n';

           }

           cout << endl;
}

Упражнение 17.26

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

(a)    Предложите синтаксис командной строки для нашей поисковой системы.

(b)   Добавьте в класс необходимые данные и функции-члены.

(c)    Предложите средства для работы с командной строкой (см. пример в разделе 7.8).

Упражнение 17.27

В качестве темы для рабочего проекта рассмотрите следующие усовершенствования нашей поисковой системы:

(a)    Реализуйте поддержку, необходимую для представления запроса AndQuery в виде одной строки, например “Motion Picture Screen Cartoonists”.



(b)   Реализуйте поддержку для ответа на запрос на основе вхождения слов не в строку, а в предложение.

(c)    Реализуйте подсистему хранения истории, с помощью которой пользователь мог бы ссылаться на предыдущий запрос по номеру, возможно, комбинируя его с новым запросом.

(d)   Вместо того чтобы показывать счетчик найденных и все найденные строки, реализуйте возможность задать диапазон выводимых строк для промежуточных вычислений и для окончательного ответа:

==> John && Jacob && Astor

(1)    john ( 3 ) lines match

(2)    jacob ( 3 ) lines match

(3)    john && jacob ( 3 ) lines match

(4)    astor ( 3 ) lines match

(5)    john && jacob && astor ( 5 ) lines match

// Новая возможность: пусть пользователь укажет, какой запрос выводить

// пользователь вводит число

==> вывести? 3

// Затем система спрашивает, сколько строк выводить

// при нажатии клавиши Enter выводятся все строки,

// но пользователь может также ввести номер одной строки или диапазон

ð                                сколько (Enter выводит все, иначе введите номер строки или диапазон) 1-3

18


Содержание раздела