Выбрать главу

== fiery || untamed

fiery ( 1 ) lines match

untamed ( 1 ) lines match

fiery || untamed ( 2 ) lines match

Requested query: fiery || untamed

( 3 ) like a fiery bird in flight. A beautiful fiery bird, he tells her,

( 4 ) magical but untamed. "Daddy, shush, there is no such thing,"

запрос "И", формулируемый с помощью оператора &&. Выводятся все строки, где оба указанных слова встречаются, причем располагаются рядом. Сюда входит и случай, когда одно слово является последним в строке, а другое - первым в следующей:

== untamed && Daddy

untamed ( 1 ) lines match

daddy ( 3 ) lines match

untamed && daddy ( 1 ) lines match

Requested query: untamed && daddy

( 4 ) magical but untamed. "Daddy, shush, there is no such thing,"

Эти элементы можно комбинировать:

fiery && bird || shyly

Однако обработка производится слева направо, и все элементы имеют одинаковые приоритеты. Поэтому наш составной запрос интерпретируется как fiery bird ИЛИ shyly, а не как fiery bird ИЛИ fiery shyly:

== fiery && bird || shyly

fiery ( 1 ) lines match

bird ( 1 ) lines match

fiery && bird ( 1 ) lines match

shyly ( 1 ) lines match

fiery && bird || shyly ( 2 ) lines match

Requested query: fiery && bird || shyly

( 3 ) like a fiery bird in flight. A beautiful fiery bird, he tells her,

( 6 ) Shyly, she asks, "I mean, Daddy, is there?"

Чтобы можно было группировать части запроса, наша система должна поддерживать скобки. Например:

fiery && (bird || shyly)

выдает все вхождения fiery bird или fiery shyly1. Результат исполнения этого запроса приведен в начале данного раздела. Кроме того, система не должна многократно отображать одну и ту же строку.

17.1. Определение иерархии классов

В этой главе мы построим иерархию классов для представления запроса пользователя. Сначала реализуем каждую операцию в виде отдельного класса:

NameQuery // Shakespeare

NotQuery // ! Shakespeare

OrQuery // Shakespeare || Marlowe

AndQuery // William && Shakespeare

В каждом классе определим функцию-член eval(), которая выполняет соответствующую операцию. К примеру, для NameQuery она возвращает вектор позиций, содержащий координаты (номера строки и колонки) начала каждого вхождения слова (см. раздел 6.8); для OrQuery строит объединение векторов позиций обоих своих операндов и т.д.

Таким образом, запрос

untamed || fiery

состоит из объекта класса OrQuery, который содержит два объекта NameQuery в качестве операндов. Для простых запросов этого достаточно, но при обработке составных запросов типа

Alice || Emma && Weeks

возникает проблема. Данный запрос состоит из двух подзапросов: объекта OrQuery, содержащего объекты NameQuery для представления слов Alice и Emma, и объекта AndQuery. Правым операндом AndQuery является объект NameQuery для слова Weeks.

AndQuery

OrQuery

NameQuery ("Alice")

NameQuery ("Emma")

NameQuery ("Weeks")

* Но левый операнд - это объект OrQuery, предшествующий оператору &&. На его месте мог бы быть объект NotQuery или другой объект AndQuery. Как же следует представить операнд, если он может принадлежать к типу любого из четырех классов? Эта проблема имеет две стороны: необходимо уметь объявлять тип операнда в классах OrQuery, AndQuery и NotQuery так, чтобы с его помощью можно было представить тип любого из четырех классов запросов;

* какое бы решение мы ни выбрали в предыдущем случае, мы должны иметь возможность вызывать соответствующий классу каждого операнда вариант функции-члена eval().

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

// не объектно-ориентированное решение

union op_type {

// объединение не может содержать объекты классов с

// ассоциированными конструкторами

NotQuery *nq;

OrQuery *oq;

AndQuery *aq;

string *word;

};

enum opTypes {

Not_query=1, O_query, And_query, Name_query

};

class AndQuery {

public:

// ...

private:

/*

* opTypes хранит информацию о фактических типах операндов запроса

* op_type - это сами операнды

*/

op_type _lop, _rop;

opTypes _lop_type, _rop_type;

};

Хранить указатели на объекты можно и с помощью типа void*:

class AndQuery {

public:

// ...

private:

void * _lop, _rop;

opTypes _lop_type, _rop_type;

};