Трансформация части контейнера с std::transform()
std::transform()⌗
Стандартная библиотека C++ STL содержит множество обобщённых и интересных инструментов для работы с контейнерами, что не удивительно, ведь одна из главных причин появления STL – это желание элегантно абстрагировать алгоритмы и отделить их от данных, т.е. от этих самых контейнеров.
Один из простых и довольно интуитивных (для функциональных программистов) алгоритмов – трансформация последовательной области контейнера. В двух словах состит он в следующем: применяем функцию (оператор) к одному элементу контейнера, пишем куда-нибудь результат (новое значение элемента), затем передвигаем итераторы обоих контейнеров вперёд и повторяем весь процесс снова. Процесс продолжается пока не закончится один из контейнеров, либо пока не будет достигнут некоторый заданный (конечный) итератор.
Алгоритм называется transform()
и находится в стандартном заголовке <algorithm>
.
template< class InputIt, class OutputIt, class UnaryOperation >
OutputIt transform( InputIt first1, InputIt last1, OutputIt d_first,
UnaryOperation unary_op );
Если first1
и last1
суть итераторы начала и конца соответственно, то унарная
(с одним параметром) функция unary_op
“пройдётся” по всем элементам
контейнера. Результат каждой такой операции будет записан назад через итератор
d_first
, который может принадлежать этому-же самому, либо совершенно другому
контейнеру, лишь-бы в нём хватило места (после записи оба итератора инкрементятся).
Аналогичная манипуляция доступна и сразу над двумя контейнерами, составляющими при “проходе” соответственно первый и второй операнд уже для бинарной операции (с двумя параметрами):
template< class InputIt1, class InputIt2, class OutputIt, class BinaryOperation >
OutputIt transform( InputIt1 first1, InputIt1 last1, InputIt2 first2,
OutputIt d_first, BinaryOperation binary_op );
Принцип автоинкремента итераторов тот-же; главное - чтобы хватило
места/элементов в контейнерах. Если один контейнер короче другого или в
контейнере для записи закончилось место, std::transform()
просто перестаёт менять
последующие елементы и возращается.
Пример использования⌗
Функцию std::tranform()
особенно приятно использовать вместе с лямбда-нотацией
(анонимные функции [](int arg, ...){ ... ; return x; }
) C++.
Вот простой пример:
#include <iostream>
#include <algorithm>
#include <vector>
/*
* Печаталка вектора
*/
template <class T, class Allocator = std::allocator<T>>
void printv(const std::vector<T, Allocator> &v) {
for (T e : v)
std::cout << e << " ";
std::cout << std::endl;
}
int main() {
std::vector<int> intv1 {10, 20, 30, 40, 50, 60};
std::vector<int> intv2 {1, 2, 3, 4, 5, 6};
std::vector<int> intv3 {1, 2, 3, 4, 5};
printv(intv1);
/*
* Унарная операция над всем контейнером; результат пишем обратно в intv1
*/
std::transform(intv1.begin(), intv1.end(), intv1.begin(),
[](int x){ return x * 10; });
printv(intv1);
/*
* Бинарная операция над двумя векторами одинаковой длины; результат в intv1
*/
std::transform(intv1.begin(), intv1.end(), intv2.begin(), intv1.begin(),
[](int a, int b) { return a + b; });
printv(intv1);
/*
* Бинарная операция над векторами разной длины: трансформация останавливается
* перед последним элементом; результат пишем обратно в intv1
*/
std::transform(intv1.begin(), intv1.end(), intv3.begin(), intv1.begin(),
[](int a, int b) { return a - b; });
printv(intv1);
return 0;
}
Скорее всего вы уже поняли, что напечатает эта программа:
10 20 30 40 50 60
100 200 300 400 500 600
101 202 303 404 505 606
100 200 300 400 500 606