Spec-Zone.ru › C++

std::partial_sum

Определён в заголовочном файле <numeric>
template< class InputIt, class OutputIt >
OutputIt partial_sum( InputIt first, InputIt last,
                      OutputIt d_first );
(1) (constexpr начиная с C++20)
template< class InputIt, class OutputIt, class BinaryOp >
OutputIt partial_sum( InputIt first, InputIt last,
                      OutputIt d_first, BinaryOp op );
(2) (constexpr начиная с C++20)
1) Если [first, last) пуст, функция ничего не делает.
В противном случае последовательно выполняются следующие действия:
  1. Создаёт аккумулятор acc, тип которого является типом значения InputIt, и инициализирует его значением *first.
  2. Присваивает acc значение *d_first.
  3. Для каждого целого числа i из [1, std::distance(first, last)) последовательно выполняет следующие действия:
a) Вычисляет acc + *iter(до C++20)std::move(acc) + *iter(начиная с C++20), где iter — следующий итератор ith для first.
b) Присваивает результат acc.
c) Присваивает acc[1] значение *dest, где dest — следующий итератор ith для d_first.
2) То же, что и (1), но вместо этого вычисляет op(acc, *iter)(до C++20)op(std::move(acc), *iter)(начиная с C++20).

Если binary_op — фактическая бинарная операция:

  • Если выполняется любое из следующих условий, программа некорректна:
    • Тип значения InputIt нельзя сконструировать из *first.
    • acc нельзя записать в d_first.
    • Результат binary_op(acc, *iter)(до C++20)binary_op(std::move(acc), *iter)(начиная с C++20) не преобразуется неявно к типу значения InputIt.
  • Если d_last — итератор, который должен быть возвращён, и выполняется любое из следующих условий, поведение не определено:
    • binary_op изменяет любой элемент [first, last) или [d_first, d_last).
    • binary_op делает недействительным любой итератор или поддиапазон в [first, last] или [d_first, d_last].
  1. Фактически присваиваемое значение является результатом присваивания на предыдущем шаге. Здесь предполагается, что результат присваивания — acc.

Параметры

first, last - пара итераторов, задающая диапазон суммируемых элементов
d_first - начало целевого диапазона; может совпадать с first
op - объект-функция бинарной операции, которая будет применена.

Сигнатура функции должна быть эквивалентна следующей:

Retfun(constType1&a,constType2&b);

Сигнатура не обязана содержать const &.
Тип Type1 должен быть таким, чтобы объект типа std::iterator_traits<InputIt>::value_type можно было неявно преобразовать к типу Type1. Тип Type2 должен быть таким, чтобы объект типа InputIt можно было разыменовать, а затем неявно преобразовать к типу Type2. Тип Ret должен быть таким, чтобы объект типа InputIt можно было разыменовать и присвоить ему значение типа Ret. ​

Требования к типам
-InputIt должен удовлетворять требованиям LegacyInputIterator.
-OutputIt должен удовлетворять требованиям LegacyOutputIterator.

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

Итератор на элемент, следующий за последним записанным элементом, или d_first, если [first, last) пуст.

Сложность

Пусть \(\scriptsize N\)N — std::distance(first, last):

1) Ровно \(\scriptsize N-1\)N-1 применений operator+.
2) Ровно \(\scriptsize N-1\)N-1 применений бинарной функции op.

Возможная реализация

partial_sum (1)
template<class InputIt, class OutputIt>
constexpr // since C++20
OutputIt partial_sum(InputIt first, InputIt last, OutputIt d_first)
{
    if (first == last)
        return d_first;
    
    typename std::iterator_traits<InputIt>::value_type sum = *first;
    *d_first = sum;
    
    while (++first != last)
    {
        sum = std::move(sum) + *first; // std::move since C++20
        *++d_first = sum;
    }
    
    return ++d_first;
    
    // or, since C++14:
    // return std::partial_sum(first, last, d_first, std::plus<>());
}
partial_sum (2)
template<class InputIt, class OutputIt, class BinaryOp>
constexpr // since C++20
OutputIt partial_sum(InputIt first, InputIt last, 
                     OutputIt d_first, BinaryOp op)
{
    if (first == last)
        return d_first;
    
    typename std::iterator_traits<InputIt>::value_type acc = *first;
    *d_first = acc;
    
    while (++first != last)
    {
        acc = op(std::move(acc), *first); // std::move since C++20
        *++d_first = acc;
    }
    
    return ++d_first;
}

Примечания

acc была введена в связи с принятием решения по вопросу LWG 539. Причина использования acc вместо непосредственного суммирования результатов (то есть *(d_first + 2) = (*first + *(first + 1)) + *(first + 2);) заключается в том, что семантика последнего становится неясной, если не совпадают следующие типы:

  • тип значения InputIt
  • типы, доступные для записи в OutputIt
  • типы параметров operator+ или op
  • тип возвращаемого значения operator+ или op

acc служит промежуточным объектом для хранения значений и передачи их на каждом шаге вычисления:

  • его тип является типом значения InputIt
  • в него выполняется запись через d_first
  • его значение передаётся в operator+ или op
  • в нём хранится возвращаемое значение operator+ или op
enum not_int { x = 1, y = 2 };

char i_array[4] = {100, 100, 100, 100};
not_int e_array[4] = {x, x, y, y};
int  o_array[4];

// OK: uses operator+(char, char) and assigns char values to int array
std::partial_sum(i_array, i_array + 4, o_array);

// Error: cannot assign not_int values to int array
std::partial_sum(e_array, e_array + 4, o_array);

// OK: performs conversions when needed
// 1. creates “acc” of type char (the value type)
// 2. the char arguments are used for long multiplication (char -> long)
// 3. the long product is assigned to “acc” (long -> char)
// 4. “acc” is assigned to an element of “o_array” (char -> int)
// 5. go back to step 2 to process the remaining elements in the input range
std::partial_sum(i_array, i_array + 4, o_array, std::multiplies<long>{});

Пример

#include <functional>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>

int main()
{
    std::vector<int> v(10, 2); // v = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}
    
    std::cout << "The first " << v.size() << " even numbers are: ";
    // write the result to the cout stream
    std::partial_sum(v.cbegin(), v.cend(), 
                     std::ostream_iterator<int>(std::cout, " "));
    std::cout << '\n';
    
    // write the result back to the vector v
    std::partial_sum(v.cbegin(), v.cend(),
                     v.begin(), std::multiplies<int>());
    
    std::cout << "The first " << v.size() << " powers of 2 are: ";
    for (int n : v)
        std::cout << n << ' ';
    std::cout << '\n';
}

Вывод:

The first 10 even numbers are: 2 4 6 8 10 12 14 16 18 20 
The first 10 powers of 2 are: 2 4 8 16 32 64 128 256 512 1024

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены задним числом к ранее опубликованным стандартам C++.

DR Применяется к Опубликованное поведение Корректное поведение
LWG 242 C++98 op не могла иметь побочных эффектов она не может изменять задействованные диапазоны
LWG 539 C++98 отсутствовали требования к типам, необходимые для корректного выполнения
вычислений результатов и присваиваний
добавлены

См. также

adjacent_difference
вычисляет разности между соседними элементами диапазона
(шаблон функции)
accumulate
суммирует или свёртывает элементы диапазона
(шаблон функции)
inclusive_scan
(C++17)
аналогична std::partial_sum, включает i-й входной элемент в i-ю сумму
(шаблон функции)
exclusive_scan
(C++17)
аналогична std::partial_sum, исключает i-й входной элемент из i-й суммы
(шаблон функции)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/algorithm/partial_sum

Spec-Zone.ru

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