Bool operator() ( intvalue ) const
{
returnvalue >= minRange && value <= maxRange;
}
private:
const intminRange, maxRange;
}
std::count_if(
v.begin(), v.end(),
Labmda_1( minRange, maxRange ) ()
);
Приведем еще один пример, в котором необходимо использовать захват переменной лямбда-выражением. На этот раз будет обрабатываться отображение цен на продукты, где ключ - название продукта, а значение - его цена. Необходимо выяснить какие продукты превышают заданный пользователем лимит цены:
#include <map>
#include <string>
#include <iostream>
#include <algorithm>
intmain ()
{
typedefstd::map< std::string, double> Prices;
Prices prices;
prices[ "Apple" ] = 9.50;
prices[ "Orange" ] = 8.30;
prices[ "Banana" ] = 2.20;
doublemaximumPriceLimit;
std::cout << "Enter price limit: ";
std::cin >> maximumPriceLimit;
std::for_each(
prices.begin(), prices.end(),
[ maximumPriceLimit ] ( constPrices::value_type & v )
{
if( v.second > maximumPriceLimit )
std::cout << "Item " << v.first << " is too expensive" << std::endl;
}
);
}
Ниже представлен вывод программы, если ввести в качестве лимита стоимость в 5.00:
В ряде других случаев лямбда-выражение должно ссылаться на внешние данные не по значению, а по ссылке. Одна из таких возможных причин - нежелание копировать большие объемы данных. Чтобы указать, что переменная захватывается по ссылке, нужно указать знак амперсанда перед именем захватываемой переменной. Тогда в сгенерированной схеме захваченные данные также будут храниться по ссылке.
В приведенной ниже программе задается набор цен на продукты, а также заказ - другое отображение, ключ которого - название продукта, а значение - заказанное количество. Программа подсчитывает общую стоимость заказа при помощи алгоритма std::accumulate и лямбда-выражения, вычисляющего стоимость конкретной записи в заказе. При этом, набор цен на продукты захватывается лямбда-выражением по ссылке:
#include <map>
#include <string>
#include <iostream>
#include <algorithm>
#include <numeric>
intmain ()
{
typedefstd::map< std::string, double > Prices;
Prices prices;
prices[ "Apple" ] = 9.50;
prices[ "Orange" ] = 8.30;
prices[ "Banana" ] = 2.20;
typedefstd::map< std::string, double > Order;
Order myOrder;
myOrder[ "Apple" ] = 2.5;
myOrder[ "Orange" ] = 1.3;
myOrder[ "Banana" ] = 0.6;
doubleorderTotalCost =
std::accumulate(
myOrder.begin(), myOrder.end(),
0.0,
[ & prices ] ( doubletotal, Order::value_type vt )
{
returntotal + vt.second * prices[ vt.first ];
}
);
std::cout << "Order totally takes: " << orderTotalCost << std::endl;
}
Если в лямбда-выражении нужно захватывать много ссылок, можно использовать объявление захвата ссылок по умолчанию. Приведенный ниже фрагмент также считает сумму заказа, однако при помощи алгоритма std::for_each. При этом захватывается две ссылки - переменная для суммы-результата и набор цен на продукты:
doubleorderTotalCost = 0.0;
std::for_each(
myOrder.begin(), myOrder.end(),
[ & ] ( constOrder::value_type & vt )
{
orderTotalCost += vt.second * prices[ vt.first ];
}
);
Аналогично, если лямбда-выражение нуждается в захвате большого количества переменных по значению, можно использовать подобный синтаксис с символом “=” вместо “&”..
Имеется также смешанный синтаксис, когда одни переменные захватываются по ссылке, а другие - по значению. Приведенная ниже модификация подсчитывает стоимость части заказа, где цена продукта превышает пользовательский лимит. Переменная-результат и набор цен на продукты захватываются по ссылке по умолчанию, а переменная с лимитом - по значению, как указано явно.
doubleorderTotalCost = 0.0;
std::for_each(
myOrder.begin(), myOrder.end(),
[ &, maxPriceLimit ] ( constOrder::value_type & vt )
{
doubleprice = prices[ vt.first ];
if( price > maxPriceLimit )
orderTotalCost += vt.second * prices[ vt.first ];
}
);
Комплексный пример
Применим полученные знания о стандартных алгоритмах, ассоциативных контейнерах и лямбда-выражениях для реальной практической задачи. Пусть имеется зарплатная ведомость организации в виде отображения, где ключ представляет собой фамилию сотрудника, а значение - его зарплата “чистыми”, т.е., после уплаты всех предусмотренных в государстве налогов. Организация желает знать какую общую сумму средств необходимо потратить вместе с налоговыми отчислениями, чтобы выдать зарплату всем сотрудникам.
Для улучшения понимания задачи, кратко рассмотрим типичный принцип расчета налоговых отчислений с заработной платы, соответствующий законодательству Украины на момент 2016г. (к сожалению, налоговые правила меняются часто, и пример приходится регулярно обновлять). Каждому сотруднику начисляется так называемая “грязная” заработная плата, или зарплата-брутто. С этой суммы удерживается подоходный налог (18%), а также, с недавних пор, военный сбор (1.5%). Сотруднику выплачивается “чистая” заработная плата, или зарплата-нетто, за вычетом этих налогов. Дополнительно, организация уплачивает с зарплаты-брутто еще один налог - единый социальный взнос (обычно, 22%, но встречаются и другие ставки, в зависимости от нюансов видов деятельности и собственности организации).
Например, пусть зарплата-брутто сотрудника равна 4000грн. С этой суммы взимается подоходный налог 720 грн.. (18%) и военный сбор 60грн (1.5%). Выдается зарплата-нетто, равная 4000-720-60, т.е. 3220 грн. Дополнительно, организация оплачивает единый социальный взнос - 22% от 4000грн, или 880 грн. Таким образом, для зарплаты-нетто в 3220 грн. уплачивается в общей сложности 1660 грн. налогов, что составляет приблизительно 51.5% от зарплаты-нетто.
В рассматриваемой задаче имеется таблица зарплат-нетто каждого из сотрудников. Соответственно, цель программы - подсчитать сумму всех зарплат-брутто + единый социальный взнос.
#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <string>
#include <functional>
intmain ()
{
typedefstd::unordered_map< std::string, double> Salaries;
Salaries salaries;
salaries[ "Ivanov" ] = 1000.0;
salaries[ "Petrov" ] = 1200.0;
salaries[ "Sidorov" ] = 800.0;
const doublepersonalIncomeTax = 0.18;
const doublesalarySocialTax = 0.22;
const doublemilitaryTax = 0.015;
doubletotalSalaryWithTaxes = 0.0;
std::for_each(
salaries.cbegin(), salaries.cend(),
[=, & totalSalaryWithTaxes ] ( constSalaries::value_type & v )
{
doublenetto = v.second;
doublepersonalBrutto = netto /
( 1.0 - personalIncomeTax - militaryTax );
doubletotalBrutto = personalBrutto * ( 1 + salarySocialTax );
totalSalaryWithTaxes += totalBrutto;
}
);
std::cout << "Total salary + taxes to pay: " << totalSalaryWithTaxes << std::endl;
}
Полные примеры из лекции
https://github.com/zaychenko-sergei/oop-samples/tree/master/lec8
Выводы
В данной лекции было продолжено изучение возможностей стандартной библиотеки шаблонов.
В первой половине лекции было введено понятие вызываемой сущности - абстракции, объединяющей конструкции языка, которые можно вызывать подобно функциям с передачей аргументов и получением результата. Была показана роль вызываемых сущностей для модификации поведения стандартных алгоритмов. Вызываемые сущности могут принимать различные формы - указатели на функции, функциональные объекты, наконец лямбда-выражения, максимально удачно вписывающиеся в синтаксис языка. Рассматривались существующие функторы, определенные в стандартной библиотеке, в частности, функторы для всех основных операторов, а также функтор std::bind, предназначенный для связывания других функторов в более сложные. Также речь зашла об универсальной вызываемой сущности std::function, которая позволяет легко состыковать обыкновенный код, не использующий шаблоны, с многообразием форм вызываемых сущностей.
Во второй половине лекции речь зашла об ассоциативных контейнерах. Рассматривались особенности работы с отображениями и множествами на основе бинарных деревьев поиска и хэш-таблиц. Были продемонстрированы возможности стыковки пользовательских типов для ключей, не нарушающие принципы работы базовых структур данных.
Наконец, были показаны дополнительные возможности лямбда-выражений, связанные с захватом внешних переменных в режимах по значению и по ссылке.
.