std::transform_reduce
Определено в заголовочном файле <numeric> | ||
|---|---|---|
| (1) | ||
template< class InputIt1, class InputIt2, class T >
T transform_reduce( InputIt1 first1, InputIt1 last1,
InputIt2 first2,
T init ); |
(с C++17) (до C++20) | |
template< class InputIt1, class InputIt2, class T >
constexpr
T transform_reduce( InputIt1 first1, InputIt1 last1,
InputIt2 first2,
T init );
| (с C++20) | |
| (2) | ||
template< class InputIt1, class InputIt2,
class T,
class BinaryReductionOp,
class BinaryTransformOp >
T transform_reduce( InputIt1 first1, InputIt1 last1,
InputIt2 first2,
T init,
BinaryReductionOp reduce,
BinaryTransformOp transform ); |
(с C++17) (до C++20) | |
template< class InputIt1, class InputIt2,
class T,
class BinaryReductionOp,
class BinaryTransformOp >
constexpr
T transform_reduce( InputIt1 first1, InputIt1 last1,
InputIt2 first2,
T init,
BinaryReductionOp reduce,
BinaryTransformOp transform );
| (с C++20) | |
| (3) | ||
template< class InputIt,
class T,
class BinaryReductionOp,
class UnaryTransformOp >
T transform_reduce( InputIt first, InputIt last,
T init,
BinaryReductionOp reduce,
UnaryTransformOp transform ); |
(с C++17) (до C++20) | |
template< class InputIt, class T,
class BinaryReductionOp,
class UnaryTransformOp >
constexpr
T transform_reduce( InputIt first, InputIt last,
T init,
BinaryReductionOp reduce,
UnaryTransformOp transform );
| (с C++20) | |
template< class ExecutionPolicy,
class ForwardIt1, class ForwardIt2, class T >
T transform_reduce( ExecutionPolicy&& policy,
ForwardIt1 first1, ForwardIt1 last1,
ForwardIt2 first2,
T init );
| (4) | (с C++17) |
template< class ExecutionPolicy,
class ForwardIt1, class ForwardIt2, class T,
class BinaryReductionOp,
class BinaryTransformOp >
T transform_reduce( ExecutionPolicy&& policy,
ForwardIt1 first1, ForwardIt1 last1,
ForwardIt2 first2,
T init,
BinaryReductionOp reduce,
BinaryTransformOp transform );
| (5) | (с C++17) |
template< class ExecutionPolicy,
class ForwardIt, class T,
class BinaryReductionOp,
class UnaryTransformOp >
T transform_reduce( ExecutionPolicy&& policy,
ForwardIt first, ForwardIt last,
T init,
BinaryReductionOp reduce,
UnaryTransformOp transform );
| (6) | (с C++17) |
std::transform_reduce(first1, last1, first2, init, std::plus<>(), std::multiplies<>());, эффективной параллелизованной версии по умолчанию std::inner_product. transform к каждой паре элементов из диапазонов [first, last) и диапазона, начинающегося с first2, и сводит результаты (возможно, переставленные и агрегированные не указанным способом) вместе с начальным значением init по reduce. transform к каждому элементу в диапазоне [first, last) и сводит результаты (возможно, переставленные и агрегированные не указанным способом) вместе с начальным значением init по reduce. policy. Эти перегрузки не участвуют в разрешении перегрузки, если |
| (до C++20) |
|
| (с C++20) |
Поведение не определено, если reduce не ассоциативно или не коммутативно.
Поведение неопределено, если reduce, или transform изменяет любой элемент или делает недействительным любой итератор в входных диапазонах, включая их конечные итераторы.
Параметры
| first, last | - | диапазон элементов, к которым применяется алгоритм |
| init | - | начальное значение обобщенной суммы |
| policy | - | используемая политика выполнения. См. политику выполнения для получения подробной информации. |
| reduce | - | бинарная FunctionObject, которая будет применяться в не определённом порядке к результатам transform, результатам других reduce и init. |
| transform | - | унарная или бинарная FunctionObject, которая будет применяться к каждому элементу входного диапазона(ов). Тип возвращаемого значения должен быть приемлем в качестве входного для reduce. |
| Требования к типу | ||
-T должен удовлетворять требованиям MoveConstructible для использования перегрузок (3,6). и результат выражений reduce(init, transform(*first)), reduce(transform(*first), init), reduce(init, init), и reduce(transform(*first), transform(*first)) должен быть преобразуем к T. |
||
-T должен удовлетворять требованиям MoveConstructible для использования перегрузок (2,5). и результат выражений reduce(init, transform(*first1, *first2)), reduce(transform(*first1, *first2), init), reduce(init, init), и reduce(transform(*first1, *first2), transform(*first1, *first2)) должен быть преобразуем к T. |
||
-InputIt должен удовлетворять требованиям LegacyInputIterator. |
||
-ForwardIt должен удовлетворять требованиям LegacyForwardIterator. |
||
Возвращаемое значение
init и transform(*first, *first2), transform(*(first + 1), *(first2 + 1)), ..., по reduce. init и transform(*first), transform(*(first + 1)), ... transform(*(last - 1)) по reduce,где обобщенная сумма GSUM(op, a1, ..., aN) определяется следующим образом:
- если N = 1, a1
- если N > 1, op(GSUM(op, b1, ..., bK), GSUM(op, bM, ..., bN)) где
- b1, ..., bN могут быть любой перестановкой a1, ..., aN и
- 1 < K + 1 = M ≤ N
другими словами, результаты transform или reduce могут быть сгруппированы и упорядочены в произвольном порядке.
Сложность
reduce и transform.transform и reduce.Исключения
Перегрузки с параметром шаблона ExecutionPolicy сообщают об ошибках следующим образом:
- Если выполнение функции, вызванной как часть алгоритма, вызывает исключение, и
ExecutionPolicy— одна из стандартных политик, вызываетсяstd::terminate. Для любой другойExecutionPolicy, поведение определяется реализацией. - Если алгоритм не может выделить память, выбрасывается
std::bad_alloc.
Примечания
В унарно-бинарной перегрузке (3,6), transform не применяется к init.
Если first == last или first1 == last1, init возвращается без изменений.
Пример
transform_reduce можно использовать для распараллеливания std::inner_product. Некоторые системы могут потребовать дополнительной поддержки для получения преимуществ от параллельного выполнения. Например, в GNU/Linux необходимо установить Intel TBB и указать опцию -ltbb для компилятора gcc/clang.
#if PARALLEL
#include <execution>
#define PAR std::execution::par,
#else
#define PAR
#endif
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <locale>
#include <numeric>
#include <vector>
// to parallelize non-associate accumulative operation, you'd better choose
// transform_reduce instead of reduce; e.g., a + b * b != b + a * a
void print_sum_squared(long const num)
{
std::cout.imbue(std::locale{"en_US.UTF8"});
std::cout << "num = " << num << '\n';
// create an immutable vector filled with pattern: 1,2,3,4, 1,2,3,4 ...
const std::vector<long> v { [n = num * 4] {
std::vector<long> v;
v.reserve(n);
std::generate_n(std::back_inserter(v), n,
[i = 0]() mutable { return 1 + i++ % 4; });
return v;
}()};
auto squared_sum = [](auto sum, auto val) { return sum + val * val; };
auto sum1 = std::accumulate(v.cbegin(), v.cend(), 0L, squared_sum);
std::cout << "accumulate(): " << sum1 << '\n';
auto sum2 = std::reduce(PAR v.cbegin(), v.cend(), 0L, squared_sum);
std::cout << "reduce(): " << sum2 << '\n';
auto sum3 = std::transform_reduce(PAR v.cbegin(), v.cend(), 0L, std::plus{},
[](auto val) { return val * val; });
std::cout << "transform_reduce(): " << sum3 << "\n\n";
}
int main()
{
print_sum_squared(1);
print_sum_squared(1'000);
print_sum_squared(1'000'000);
}Возможный вывод:
num = 1 accumulate(): 30 reduce(): 30 transform_reduce(): 30 num = 1,000 accumulate(): 30,000 reduce(): -7,025,681,278,312,630,348 transform_reduce(): 30,000 num = 1,000,000 accumulate(): 30,000,000 reduce(): -5,314,886,882,370,003,032 transform_reduce(): 30,000,000 // Compile-options for parallel execution on POSIX: // g++ -O2 -std=c++17 -Wall -Wextra -pedantic -DPARALLEL ./example.cpp -ltbb -o tr; ./tr
См. также
| суммирует или сворачивает диапазон элементов (функция-шаблон) |
|
| применяет функцию к диапазону элементов, сохраняя результаты в целевом диапазоне (функция-шаблон) |
|
|
(C++17) | аналогично std::accumulate, но может выполняться не в порядке (функция-шаблон) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/algorithm/transform_reduce