vector vf; // Создать контейнер типа float

…                 // и заполнить его данными

float product =                   // Присвоить product результат

 accumulate(vf.begin, vf.end, // вызова multiplies

 1.0, multples);         // для каждого элемента vf

                                  // с начальным значением 1.0

Только не забудьте о том, что начальное значение вместо нуля должно быть равно единице (в вещественном формате, не в int!). Если начальное значение равно нулю, то результат всегда будет равен нулю — ноль, умноженный на любое число, остается нулем.

Последний пример не столь тривиален. В нем вычисляется среднее арифметическое по интервалу точек, представленных структурами следующего вида:

struct Point {

 Point(double initX, double initY): x(initX), y(initY) {}

 double x, y;

};

В этом примере обобщающей функцией будет функтор PointAverage, но перед рассмотрением класса этого функтора стоит рассмотреть его использование при вызове accumulate:

list lp;

Point avg =                       // Вычисление среднего

 accumulate(lp.begin, lp.end, // арифметического по точкам,

 Point(0,0), PointAverage);     // входящим в список lр

Просто и бесхитростно, как и должно быть. На этот раз в качестве начального значения используется объект Point, соответствующий началу координат, а нам остается лишь помнить о необходимости исключения этой точки из вычислений.

Функтор PointAverage отслеживает количество обработанных точек, а также суммы их компонентов x и y. При каждом вызове он обновляет данные и возвращает средние координаты по обработанным точкам. Поскольку для каждой точки в интервале функтор вызывается ровно один раз, он делит суммы по составляющим x и y на количество точек в интервале. Начальная точка, переданная при вызове accumulate, игнорируется.

class PointAverage:

 publiс binary_function {

public:

 PointAverage: xSum(0), ySum(0), numPoints(0) {}

 const Point operator (const Point& avgSoFar, const Point& p) {

  ++numPoints;

  xSum += p.x;

  ySum += p.y;

  return Point(xSum/numPoints, ySum/numPoints);

 }

private:

 size_t numPoints;

 double xSum;

 double ySum;

};

Такое решение прекрасно работает, и лишь из-за периодических контактов с неординарно мыслящими личностями (многие из которых работают в Комитете по стандартизации) я могу представить себе реализации STL, в которых возможны проблемы. Тем не менее, PointAverage нарушает параграф 2 раздела 26.4.1 Стандарта, который, как вы помните, запрещает побочные эффекты по отношению к функции,передаваемой accumulate. Модификация переменных numPoints, xSum и ySum относится к побочным эффектам, поэтому с технической точки зрения приведенный выше фрагмент приводит к непредсказуемым последствиям. На практике трудно представить, что приведенный код может не работать, но чтобы моя совесть была чиста, я обязан специально оговорить это обстоятельство.

Впрочем, у меня появляется удобная возможность упомянуть о for_each — другом алгоритме, который может использоваться для обобщения интервалов. На for_each не распространяются ограничения, установленные для accumulate. Алгоритм for_each, как и accumulate, получает интервал и функцию (обычно в виде объекта функции), вызываемую для каждого элемента в интервале, однако функция, передаваемая for_each, получает только один аргумент (текущий элемент интервала), а после завершения работы for_each возвращает свою функцию (а точнее, ее копию — см. совет 38). Что еще важнее, переданная (и позднее возвращаемая) функция может обладать побочными эффектами.

Помимо побочных эффектов между for_each и accumulate существуют два основных различия. Во-первых, само название accumulate ассоциируется с вычислением сводного значения по интервалу, а название for_each скорее предполагает выполнение некой операции с каждым элементом интервала. Алгоритм for_each может использоваться дя вычисления сводной величины, но такие решения по наглядности уступают accumulate.

Перейти на страницу:

Похожие книги