Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 238 239 240 241 242 243 244 245 246 ... 297
Перейти на страницу:

Операторы сравнения и равенства кортежей ведут себя подобно соответствующим операторам контейнеров (см. раздел 9.2.7). Эти операторы выполняются для членов двух кортежей, слева и справа. Сравнить два кортежа можно только при совпадении количества их членов. Кроме того, чтобы использовать операторы равенства или неравенства, должно быть допустимо сравнение каждой пары членов при помощи оператора ==; а для использования операторов сравнения допустимым должно быть использование оператора <. Например:

tuple<string, string> duo("1", "2");

tuple<size_t, size_t> twoD(1, 2);

bool b = (duo == twoD); // ошибка: нельзя сравнить size_t и string

tuple<size_t, size_t, size_t> threeD(1, 2, 3);

b = (twoD < threeD);    // ошибка: разное количество членов

tuple<size_t, size_t> origin(0, 0);

b = (origin < twoD);    // ok: b — это true

Поскольку кортеж определяет операторы < и ==, последовательности кортежей можно передавать алгоритмам, а также использовать кортеж как тип ключа в упорядоченном контейнере.

Упражнения раздела 17.1.1

Упражнение 17.1. Определите кортеж, содержащий три члена типа int, и инициализируйте их значениями 10, 20 и 30.

Упражнение 17.2. Определите кортеж, содержащий строку, вектор строки и пару из строки и целого числа (типы string, vector<string> и pair<string, int>).

Упражнение 17.3. Перепишите программы TextQuery из раздела 12.3 так, чтобы использовать кортеж вместо класса QueryResult. Объясните, что на ваш взгляд лучше и почему.

17.1.2. Использование кортежей для возвращения нескольких значений

Обычно кортеж используют для возвращения из функции нескольких значений. Например, рассматриваемый книжный магазин мог бы быть одним из нескольких магазинов в сети. У каждого магазина был бы транзакционный файл, содержащий данные по каждой проданной книге. В этом случае могло бы понадобиться просмотреть все продажи данной книги по всем магазинам.

Предположим, для каждого магазина имеется файл транзакций. Каждый из этих транзакционных файлов в магазине будет содержать все транзакции для каждой группы книг. Предположим также, что некая другая функция читает эти транзакционные файлы, создает вектор vector<Sales_data> для каждого магазина и помещает эти векторы в вектор векторов:

// каждый элемент в файле содержит транзакции

// для определенного магазина

vector<vector<Sales_data>> files;

Давайте напишем функцию, которая будет просматривать файлы в поисках магазина, продавшего заданную книгу. Для каждого магазина, у которого есть соответствующая транзакция, необходимо создать кортеж для содержания индекса этого магазина и двух итераторов. Индекс будет позицией соответствующего магазина в файлах, а итераторы отметят первую и следующую после последней записи по заданной книге в векторе vector<Sales_data> этого магазина.

Функция, возвращающая кортеж

Для начала напишем функции поиска заданной книги. Аргументами этой функции будет только что описанный вектор векторов и строка, представляющая ISBN книги. Функция будет возвращать вектор кортежей с записями по каждому магазину, где была продана по крайней мере одна заданная книга:

// matches имеет три члена: индекс магазина и итераторы в его векторе

typedef tuple<vector<Sales_data>::size_type,

              vector<Sales_data>::const_iterator,

              vector<Sales_data>::const_iterator> matches;

// files хранит транзакции по каждому магазину

// findBook() возвращает вектор с записями для каждого магазина,

// продавшего данную книгу

vector<matches>

findBook(const vector<vector<Sales_data>> &files,

         const string &book) {

 vector<matches> ret; // изначально пуст

 // для каждого магазина найти диапазон, соответствующий книге

 // (если он есть)

 for (auto it = files.cbegin(); it != files.cend(); ++it) {

  // найти диапазон Sales_data с тем же ISBN

  auto found = equal_range(it->cbegin(), it->cend(),

                           book, compareIsbn);

  if (found.first != found.second) // у этого магазина есть продажи

   // запомнить индекс этого магазина и диапазона соответствий

   ret.push_back(make_tuple(it - files.cbegin(),

                            found.first, found.second));

 }

 return ret; // пуст, если соответствий не найдено

}

Цикл for перебирает элементы вектора files, которые сами являются векторами. В цикле for происходит вызов библиотечного алгоритма equal_range(), работающего как одноименная функция-член ассоциативного контейнера (см. раздел 11.3.5). Первые два аргумента функции equal_range() являются итераторами, обозначающими исходную последовательность (см. раздел 10.1). Третий аргумент — значение. По умолчанию для сравнения элементов функция equal_range() использует оператор <. Поскольку тип Sales_data не имеет оператора <, передаем указатель на функцию compareIsbn() (см. раздел 11.2.2).

Алгоритм equal_range() возвращает пару итераторов, обозначающих диапазон элементов. Если книга не будет найдена, то итераторы окажутся равны, означая, что диапазон пуст. В противном случае первый член возвращенной пары обозначит первую соответствующую транзакцию, а второй — следующую после последней.

Использование возвращенного функцией кортежа

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

void reportResults(istream &in, ostream &os,

                   const vector<vector<Sales_data>> &files) {

 string s; // искомая книга

 while (in >> s) {

  auto trans = findBook(files, s);

  // магазин, продавший эту книгу

  if (trans.empty()) {

   cout << s << " not found in any stores" << endl;

   continue; // получить следующую книгу для поиска

  }

  for (const auto &store : trans) // для каждого магазина с

                                  // продажей

   // get<n> возвращает указанный элемент кортежа в store

   os << "store " << get<0>(store) << " sales: "

      << accumulate(get<1>(store), get<2>(store),

         Sales_data(s))

      << endl;

 }

}

Цикл while последовательно читает поток istream по имени in, чтобы запустить обработку следующей книги. Вызов функции findBook() позволяет выяснить, присутствует ли строка s, и присваивает результаты вектору trans. Чтобы упростить написание типа trans, являющегося вектором кортежей, используем ключевое слово auto.

Если вектор trans пуст, значит, по книге s никаких продаж не было. В таком случае выводится сообщение и происходит возврат к циклу while, чтобы обработать следующую книгу.

Цикл for свяжет ссылку store с каждым элементом вектора trans. Поскольку изменять элементы вектора trans не нужно, объявим ссылку store ссылкой на константу. Для вывода результатов используем get: get<0> — индекс соответствующего магазина; get<1> — итератор на первую транзакцию; get<2> — на следующую после последней.

Поскольку класс Sales_data определяет оператор суммы (см. раздел 14.3), для суммирования транзакций можно использовать библиотечный алгоритм accumulate() (см. раздел 10.2.1). Как отправную точку суммирования используем объект класса Sales_data, инициализированный конструктором Sales_data(), получающим строку (см. раздел 7.1.4). Этот конструктор инициализирует переменную-член bookNo переданной строкой, а переменные-члены units_sold и revenue — нулем.

Упражнения раздела 17.1.2

Упражнение 17.4. Напишите и проверьте собственную версию функции findBook().

Упражнение 17.5. Перепишите функцию findBook() так, чтобы она возвращала пару, содержащую индекс и пару итераторов.

Упражнение 17.6. Перепишите функцию findBook() так, чтобы она не использовала кортеж или пару.

Упражнение 17.7. Объясните, какую версию функции findBook() вы предпочитаете и почему.

Упражнение 17.8. Что будет, если в качестве третьего параметра алгоритма accumulate() в последнем примере кода этого раздела передать объект класса Sales_data?

1 ... 238 239 240 241 242 243 244 245 246 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии