Читать книги » Книги » Компьютеры и Интернет » Программирование » C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Читать книгу C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц, Яцек Галовиц . Жанр: Программирование.
C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц
Название: C++17 STL Стандартная библиотека шаблонов
Дата добавления: 15 июль 2023
Количество просмотров: 1 473
(18+) Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних просмотр данного контента СТРОГО ЗАПРЕЩЕН! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту для удаления материала.
Читать онлайн

C++17 STL Стандартная библиотека шаблонов читать книгу онлайн

C++17 STL Стандартная библиотека шаблонов - читать онлайн , автор Яцек Галовиц

С++ — объектно-ориентированный язык программирования, без которого сегодня немыслима промышленная разработка ПО. В этой замечательной книге описана работа с контейнерами, алгоритмами, вспомогательными классами, лямбда-выражениями и другими интересными инструментами, которыми богат современный С++. Освоив материал, вы сможете коренным образом пересмотреть привычный подход к программированию.
Преимущество издания — в подробном описании стандартной библиотеки шаблонов С++, STL. Ее свежая версия была выпущена в 2017 году. В книге вы найдете более 90 максимально реалистичных примеров, которые демонстрируют всю мощь STL. Многие из них станут базовыми кирпичиками для решения более универсальных задач.
Вооружившись этой книгой, вы сможете эффективно использовать С++17 для создания высококачественного и высокопроизводительного ПО, применимого в различных отраслях.

1 ... 55 56 57 58 59 ... 121 ВПЕРЕД
Перейти на страницу:
сложная часть — генерация сигналов. Еще раз взглянем на gen_cosine:

static auto gen_cosine (size_t period_len)

{

  return [period_len, n{0}] () mutable {

    return cos(double(n++) * 2.0 * M_PI / period_len);

  };

}

Каждый экземпляр лямбда-выражения представляет собой объект функции, который изменяет свое состояние при каждом вызове. Его состояние описывается переменными period_len и n. Последняя изменяется с каждым вызовом. Сигнал имеет различные значения в разные моменты времени, выражение n++ описывает увеличивающиеся моменты времени. Чтобы получить сам вектор сигнала из выражения, мы создали вспомогательную функцию signal_from_generator:

template <typename F>

static auto signal_from_generator(size_t len, F gen)

{

  csignal r (len);

  generate(begin(r), end(r), gen);

  return r;

}

Эта вспомогательная функция выделяет память для вектора сигнала с заданной длиной и вызывает метод std::generate, что позволяет заполнить точки его графика. Для каждого элемента вектора r он один раз вызывает объект функции gen, который представляет собой самоизменяющийся объект функции; его можно создать с помощью gen_cosine.

 

 К сожалению, способ решения задачи с помощью STL не позволяет сделать код более элегантным. Ситуация может измениться, если библиотека ranges будет включена в клуб STL (надо надеяться, что это случится в C++20). 

Определяем ошибку суммы двух векторов

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

Существует простая формула для определения этой ошибки между сигналами a и b (рис. 6.5).

Для каждого значения i мы вычисляем a[i]–b[i], возводим разность в квадрат (таким образом, получаем возможность сравнить положительные и отрицательные значения) и, наконец, складываем эти значения. Опять же мы могли бы просто воспользоваться циклом, но ради интереса сделаем это с помощью алгоритма STL. Плюс данного подхода заключается в том, что мы не зависим от структуры данных. Наш алгоритм будет работать для векторов и для спископодобных структур данных, для которых нельзя выполнить прямое индексирование.

Как это делается

В этом примере мы создадим два сигнала и посчитаем для них ошибку суммы.

1. Как и обычно, сначала приводим выражения include. Затем объявляем об использовании пространства имен std:

#include <iostream>

#include <cmath>

#include <algorithm>

#include <numeric>

#include <vector>

#include <iterator>

using namespace std;

2. Определим ошибку суммы двух сигналов. Таковыми выступят синусоидальная волна и ее копия, только оригинал будет сохранен в векторе, содержащем переменные типа double, а копия — в векторе, включающем переменные типа int. Поскольку копирование значения из переменной типа double в переменную типа int приводит к потере той его части, что стоит после десятичной точки, мы потеряем какие-то данные. Назовем содержащий переменные типа double вектор as, что расшифровывается как analog signal (аналоговый сигнал), а вектор, который содержит значения типа int, — ds, что значит digital signal (цифровой сигнал). Ошибка суммы позднее покажет, насколько велики потери данных.

int main()

{

  const size_t sig_len {100};

  vector<double> as (sig_len); // a для аналогового сигнала

  vector<int> ds (sig_len);    // d для цифрового сигнала

3. Чтобы сгенерировать сигнал синусоидальной волны, реализуем небольшое лямбда-выражение, имеющее изменяемое значение счетчика n. Его можно вызывать по мере необходимости, и при каждом вызове оно будет возвращать значение для следующей временной точки синусоидальной волны. Вызов std::generate заполняет вектор сигнала, а вызов std::copy копирует все значения из вектора переменных типа double в вектор переменных типа int.

  auto sin_gen ([n{0}] () mutable {

    return 5.0 * sin(n++ * 2.0 * M_PI / 100);

  });

  generate(begin(as), end(as), sin_gen);

  copy(begin(as), end(as), begin(ds));

4. Сначала выведем на экран сигналы, чтобы позднее можно было построить для них график:

copy(begin(as), end(as),

  ostream_iterator<double>{cout, " "});

cout << 'n';

copy(begin(ds), end(ds),

  ostream_iterator<double>{cout, " "});

cout << 'n';

5. Теперь перейдем к самой ошибке суммы. Используем метод std::inner_product, поскольку его можно легко адаптировать для определения разницы между двумя соответствующими элементами вектора сигналов. Он будет итерировать по обоим диапазонам данных, выбирать элементы в соответствующих позициях диапазонов, рассчитывать их разность и складывать результат.

  cout << inner_product(begin(as), end(as), begin(ds),

                        0.0, std::plus<double>{},

                        [](double a, double b) {

                          return pow(a - b, 2);

                        })

       << 'n';

}

6. Компиляция и запуск программы приведут к выводу двух длинных строк, содержащих сигналы, и третьей строки, в которой показывается единственное значение — разность сигналов. Эта разность равна 40.889. Если бы мы рассчитывали ошибку непрерывно, сначала для первой пары элементов, затем — для первых двух пар, затем — для первых трех и т.д., то получили бы накопленную кривую ошибок. Ее можно увидеть на построенном графике (рис. 6.6).

Как это работает

В данном примере мы решили задачу прохода в цикле по двум векторам, получения разности между их соответствующими значениями, возведения их в квадрат и суммирования этих значений с помощью одного вызова std::inner_product. В то же время единственным кодом, который мы написали, стало лямбда-выражение [](double a,double b) { return pow(a-b,2);}, принимающее разность аргументов и возводящее ее в квадрат.

Взглянув на потенциальную реализацию метода std::inner_product, можно увидеть, как и почему это работает:

template<class InIt1, class InIt2, class T, class F, class G>

T inner_product(InIt1 it1, InIt1 end1, InIt2 it2, T val,

                F bin_op1, G bin_op2)

{

  while (it1 != end1) {

    val = bin_op1(val, bin_op2(*it1, *it2));

    ++it1;

    ++it2;

  }

  return value;

}

Алгоритм принимает пару итераторов (начальный и конечный) для первого диапазона данных, а также начальный итератор для второго диапазона. В нашем случае они представляют собой векторы, для которых нужно определить ошибку суммы. Следующий символ — исходное значение val. Мы

1 ... 55 56 57 58 59 ... 121 ВПЕРЕД
Перейти на страницу:
Комментарии (0)