|
Программирование >> Включение нужных заголовков
Как и многие другие алгоритмы, 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 возвращает свою функцию (а точнее.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |