}
Тип результата (суммы) совпадает с типом переменной, которую алгоритм
accumulate()
использует в качестве аккумулятора. Это обеспечивает высокую степень гибкости которая может играть важную роль. Рассмотрим пример.
void f(int* p,int n)
{
int s1 = accumulate(p, p+n, 0); // суммируем целые числа в int
long sl = accumulate(p, p+n, long(0)); // суммируем целые числа
// в long
double s2 = accumulate(p, p+n, 0.0); // суммируем целые числа
// в double
}
На некоторых компьютерах переменная типа
long
состоит из гораздо большего количества цифр, чем переменная типа
int
. Переменная типа
double
может представить большие (и меньшие) числа, чем переменная типа
int
, но, возможно, с меньшей точностью. В главе 24 мы еще вернемся к вопросу о диапазоне и точности в вычислениях.
Использование переменной
init
в качестве аккумулятора представляет собой весьма распространенную идиому, позволяющую задать тип аккумулятора.
void f(vector<double>& vd,int* p,int n)
{
double s1 = 0;
s1 = accumulate(vd.begin(),vd.end(),s1);
int s2 = accumulate(vd.begin(), vd.end(),s2); // Ой
float s3 = 0;
accumulate(vd.begin(), vd.end(), s3); // Ой
}
Не забудьте инициализировать аккумулятор и присвоить результат работы алгоритма
accumulate()
какой-нибудь переменной. В данном примере в качестве инициализатора использовалась переменная
s2
, которая сама еще не получила начальное значение до вызова алгоритма; результат такого вызова будет непредсказуем. Мы передали переменную
s3
алгоритму
accumulate()
(по значению; см. раздел 8.5.3), но результат ничему не присвоили; такая компиляция представляет собой простую трату времени.
21.5.2. Обобщение алгоритма accumulate()
Итак, основной алгоритм
accumulate()
с тремя аргументами выполняет суммирование. Однако существует много других полезных операций, например умножение и вычитание, которые можно выполнять над последовательностями, поэтому в библиотеке STL предусмотрена версия алгоритма
accumulate()
с четырьмя аргументами, позволяющая задавать используемую операцию.
template<class In, class T, class BinOp>
T accumulate(In first, In last, T init, BinOp op)
{
while (first!=last) {
init = op(init, *first);
++first;
}
return init;
}
Здесь можно использовать любую бинарную операцию, получающую два аргумента, тип которых совпадает с типом аккумулятора. Рассмотрим пример.
array<double,4> a = { 1.1, 2.2, 3.3, 4.4 }; // см. раздел 20.9
cout << accumulate(a.begin(),a.end(), 1.0, multiplies<double>());
Этот фрагмент кода выводит на печать число 35.1384, т.е. 1.0*1.1*2.2*3.3*4.4 (1.0 — начальное значение). Бинарный оператор
multiplies<double>()
, передаваемый как аргумент, представляет собой стандартный объект-функцию, выполняющий умножение; объект-функция
multiplies<double>
перемножает числа типа
double
, объект-функция
multiplies<int>
перемножает числа типа
int
и т.д. Существуют и другие бинарные объекты-функции:
plus
(сложение),
minus
(вычитание),
divides
и
modulus
(вычисление остатка от деления). Все они определены в заголовке
<functional>
(раздел Б.6.2).
Обратите внимание на то, что для умножения чисел с плавающей точкой естественным начальным значением является число
1.0
. Как и в примере с алгоритмом
sort()
(см. раздел 21.4.2), нас часто интересуют данные, хранящиеся в объектах классов, а не обычные данные встроенных типов. Например, мы могли бы вычислить общую стоимость товаров, зная стоимость их единицы и общее количество.
struct Record {
double unit_price;
int units; // количество проданных единиц
// ...
};
Мы можем поручить какому-то оператору в определении алгоритма
accumulate
извлекать данные units из соответствующего элемента класса
Record
и умножать на значение аккумулятора.
double price(double v,const Record& r)
{
return v + r.unit_price * r.units; // вычисляет цену
// и накапливает итог
}
void f(const vector<Record>& vr)
{
double total = accumulate(vr.begin(),vr.end(),0.0,price);
// ...