Программирование >>  Включение нужных заголовков 

1 ... 42 43 44 [ 45 ] 46 47 48 ... 71


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

list<double> Id: Создать список и заполнить

несколькими значениями типа double.

double sum = accumulatedd.beginO.ld.endO.O.O): Вычислить сумму чисел

с начальным значением 0.0

Обратите внимание: в приведенном примере начальное значение задается в форме 0.0. Эта подробность важна. Число 0.0 относится к типу doubl е, поэтому accumul ate использует для хранения вычисляемой суммы переменную типа double. Предположим, вызов выглядит следующим образом:

double sum = accumulate(ld.begin(),ld.end(),0): Вычисление суммы чисел

с начальным значением 0; неправильно!

В качестве начального значения используется int О, поэтому accumulate накапливает вычисляемое значение в переменной типа int. В итоге это значение будет возвращено алгоритмом accumul ate и использовано для инициализации переменной sum. Программа компилируется и работает, но значение sum будет неправильным. Вместо настоящей суммы списка чисел типа doubl е переменная содержит сумму всех чисел, преобразуемую к int после каждого суммирования.

Алгоритм accumul ate работает только с итераторами ввода и поэтому может использоваться даже с istreami terator и istreambufjterator (см. совет 29):

cout The sum of the ints on the standard input is Вывести сумму accumulate(istream iterator<int>(cin), чисел из входного

istreamJterator<int>(), потока

Из-за своей первой, стандартной формы алгоритм accumul ate был отнесен к числовым алгоритмам. Но существует и другая, альтернативная форма, которой при вызове передается начальное значение и произвольная обобщающая функция. В этом варианте алгоритм accumul ate становится гораздо более универсальным.

В качестве примера рассмотрим возможность применения accumulate для вычисления суммы длин всех строк в контейнере. Для вычисления суммы алгоритм должен знать две вещи: начальное значение суммы (в данном случае 0) и функцию обновления суммы для каждой новой строки. Следующая функция берет предыдущее значение суммы, прибавляет к нему длину новой строки и возвращает обновленную сумму:

string::sizejype См. далее

stringLengthSumCstring: :sizejype sumSoFar, const strings s)

return sumSoFar + s.sizeO:

Тело функции убеждает в том, что происходящее весьма тривиально, но на первый взгляд смущают объявления string: :size type. На самом деле в них нет



ничего страшного. У каждого стандартного контейнера STL имеется определение типа size type, относящееся к счетному типу данного контейнера. В частности, значение этого типа возвращается функцией size. Для всех стандартных контейнеров определение si ze type должно совпадать с si ze t, хотя теоретически нестандартные STL-совместимые контейнеры могут использовать в sizetype другой тип (хотя я не представляю, для чего это может понадобиться). Для стандартных контейнеров запись контейнер:: si ze type можно рассматривать как специальный синтаксис для size t.

Функция stringLengthSum является типичным представителем обобщающих функций, используемых при вызове accumulate. Функция ползает текущее значение суммы и следующий элемент интервала, а возвращает новое значение накапливаемой суммы. Накапливаемая сумма (сумма длин строк, встречавшихся ранее) относится к типу string: :size type, а обрабатываемый элемент относится к типу string. Как это часто бывает, возвращаемое значение относится к тому же типу, что и первый параметр функции.

Функция StringLengthSum используется в сочетании с accumulate следующим образом:

set<string> ss: Создать контейнер строк

и заполнить его данными string::size type lengthSum = Присвоить lengthSum

accumulate(ss.beginO.ss.endO, результат вызова stringLengthSum 0,StringLengthSum); для каждого элемента ss

с нулевым начальным значением

Изящно, не правда ли? Произведение вычисляется еще проще, поскольку вместо написания собственной функции суммирования можно обойтись стандартным функтором mul ti pi i es:

vector<float> vf: Создать контейнер типа float

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

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

accumulate(vf.begin(),vf.end(), вызова multiplies<float> 1.0,multiplies<float>()); для каждого элемента vf с начальным значением 1.0

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

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

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

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



list<Point> Ip:

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

accumu1ate(1p.begin(),1p.end(). арифметического по точкам.

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

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

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

class PointAverage:

риЫ iс hinary function<Point.Point, Point>{ public:

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

const Point operatorO (const Points avgSoFar, const Points p) {

++numPoints: xSum += p.x: ySum += p.y:

return Poi nt(xSum/numPoi nts.ySum/numPoi nts):

private: size t numPoints: double xSum: double ySum:

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

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



1 ... 42 43 44 [ 45 ] 46 47 48 ... 71

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика