Spec-Zone.ru › C++

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)
1) Эквивалентно std::transform_reduce(first1, last1, first2, init, std::plus<>(), std::multiplies<>());, эффективной параллелизованной версии по умолчанию std::inner_product.
2) Применяет transform к каждой паре элементов из диапазонов [first, last) и диапазона, начинающегося с first2, и сводит результаты (возможно, переставленные и агрегированные не указанным способом) вместе с начальным значением init по reduce.
3) Применяет transform к каждому элементу в диапазоне [first, last) и сводит результаты (возможно, переставленные и агрегированные не указанным способом) вместе с начальным значением init по reduce.
4-6) То же самое, что (1-3), но выполняется в соответствии с policy. Эти перегрузки не участвуют в разрешении перегрузки, если

std::is_execution_policy_v<std::decay_t<ExecutionPolicy>> является true.

(до C++20)

std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> является true.

(с 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.

Возвращаемое значение

2) Обобщенная сумма init и transform(*first, *first2), transform(*(first + 1), *(first2 + 1)), ..., по reduce.
3) Обобщенная сумма 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 могут быть сгруппированы и упорядочены в произвольном порядке.

Сложность

1,2,4,5) O(last1 - first1) применений reduce и transform.
3,6) O(last - first) применений 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

См. также

accumulate
суммирует или сворачивает диапазон элементов
(функция-шаблон)
transform
применяет функцию к диапазону элементов, сохраняя результаты в целевом диапазоне
(функция-шаблон)
reduce
(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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API