Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Double operator() ( doublex, doubley ) const




// ^ Аргументы как аргументы оригинальной лямбды

{

return 3.0 * x / (y + 2.0); // <- Тело как в теле оригинальной лямбды

}

};

 

std::transform(

vX.begin(), vX.end(), vY.begin(),

std::back_inserter(vR),

Labmda_1()

);

 

Итак, ЛЯМБДА-выражение в языке С++ - это неименованный функциональный объект, тело и код создания которого автоматически генерируется компилятором на основе заданных программистом инструкций. Лямбда-выражения с подходящими форматами аргументов и возвращаемым типом могут быть использованы во всех конструкциях, где могут использоваться вызываемые сущности.

 

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

 

Эту же идею можно применить для упрощения ранее созданной вспомогательной функции для вывода содержимого последовательности на экран - предоставляемое лямбда-выражение максимально простым способом задает что именно нужно сделать с каждым из элементов перебираемой последовательности:

 

template < typename InputIt >

void printCollection (InputIt _first, InputIt _last, const char * _prefix)

{

std::cout << _prefix;

std::for_each(_first, _last,
[] (typename InputIt::value_type x)
{
std::cout << x << ' ';
}
);

std::cout << std::endl;

}

 

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

 

std::sort(

employees.begin(), employees.end(),

[] (const Employee * _pEmployee1, const Employee * _pEmployee2) -> bool

{

if (_pEmployee1->getName() < _pEmployee2->getName())

return true;

 

else if (_pEmployee1->getName() == _pEmployee2->getName())

return _pEmployee1->getAge() < _pEmployee2->getAge();

 

Else

return false;

}

);

 

Очевидно, применение лямбд намного привлекательнее прежних устаревающих способов записи и особенно комбинирования функциональных объектов. В целом, лямбда-выражения значительно разгружают сложность использования функциональных объектов для управления поведением алгоритмов. Это делает более привлекательным также и использование стандартных алгоритмов STL, ранее скованное сложностью записи каких-либо нетривиальных предикатов для реально встречающихся на практике случаев.

 

При необходимости, лямбда-выражения могут быть вложенными, однако не стоит увлекаться написанием настолько сложного для восприятия кода. Обычно, крупное лямбда-выражение стоит разбить на несколько вспомогательных функций.

 

Std::function

 

Пусть в программе имеется три варианта сортировки данных о сотрудниках:

● в алфавитном порядке по фамилии;

● в порядке убывания зарплаты;

● в порядке убывания возраста.

 

Предположим, выбор способа сортировки зависит от какой-либо внешней настройки, например, от аргумента командной строки или свойства в конфигурационном файле. Для упрощения, возьмем, что способ сортировки передается в подпрограмму в виде enum-аргумента:

 

enum class EmployeeSortMode

{

Alphabetical,

Salaries,

Age

};

 

Сортировку поместим во вспомогательную функцию, принимающую ссылку на вектор сотрудников, а также выбранный режим сортировки. Простое решение состоит в использовании блока switch для выполнения различных способов сортировки в зависимости от выбранного режима:

 

void sortEmployees (

std::vector< Employee * > & _employees,

EmployeeSortMode _sortMode

)

{

// Выбираем способ сортировки в зависимости от режима

switch (_sortMode)

{

// Сортировка по алфавиту

case EmployeeSortMode::Alphabetical:

std::sort(

_employees.begin(), _employees.end(),

[] (const Employee * _pEmployee1,
const Employee * _pEmployee2)

{

return _pEmployee1->getName() < _pEmployee2->getName();

}

);

break;

 

// Сортировка по возрасту

case EmployeeSortMode::Age:

std::sort(

_employees.begin(), _employees.end(),

[] (const Employee * _pEmployee1,
const Employee * _pEmployee2)

{

return _pEmployee1->getAge() > _pEmployee2->getAge();

}

);

break;

 

// Сортировка по зарплате

case EmployeeSortMode::Salaries:

std::sort(

_employees.begin(), _employees.end(),

[] (const Employee * _pEmployee1,
const Employee * _pEmployee2)

{

return _pEmployee1->getSalary() > _pEmployee2->getSalary();

}

);

break;

 

// Неизвестный режим сортировки

default:

throw std::logic_error("Unexpected sort mode");

}

}

 

Такой вариант решения содержит немало повторяющегося кода по вызову алгоритма sort. По сути, между режимами сортировки отличается только лямбда-выражение, определяющее критерий сравнения сотрудников. Для упрощения кода имеет смысл выделить выбор предиката в зависимости от режима в отдельную функцию, а в данной функции вызывать сортировку лишь однократно.

 

Это намерение моментально наталкивается на вопрос - а какой тип имеют эти лямбда-выражения? До лямбда-выражений мы бы выбирали бы функцию сравнения при помощи указателя на функцию определенной сигнатуры, однако каждый функциональный объект и, соответственно, каждое лямбда-выражение, имеют свой уникальный тип. В случае лямбда-выражений этот тип еще и полностью зависит от компилятора. В контексте шаблонов это никого не смущает, поскольку тип лямбда-выражения нигде в коде явно не указывается. В нашей же ситуации необходимо записать какой-то конкретный тип для возврата лямбда-выражения из вспомогательной функции, который подойдет к каждому из 3 лямбда-выражений.

 

В стандартной библиотеке имеется специальное средство std::function, представляющее собой универсальную абстракцию вызываемой сущности определенного формата. Объявление для такого объекта выглядит следующим довольно необычным образом (все же, немного симпатичней, чем указатели на функции):

 

std::function< bool (const Employee *, const Employee *) > comparator;

^ ^

возвращаемый -------- формальные аргументы -----
тип

 

Объект std::function можно инициализировать одним из возможных вариантов вызываемой сущности:

 

● указателем на функцию:

 

bool MyEmployeeCompareFunction (const Employee *, const Employee *);

comparator = & myEmployeeCompareFunction;

 

● функциональным объектом:


struct MyEmployeeCompareFunctor

{

bool operator () (const Employee *, const Employee *) const;

};

 

comparator = & MyEmployeeCompareFunctor();

 

● лямбда-выражением:

 

comparator = [] (const Employee *, const Employee *) { … }

 

После инициализации объект std::function можно свободно передавать по значению или по ссылке. Окружающий std::function код вовсе не обязан быть шаблонным. Для вызова используется стандартный функциональный синтаксис:

 

bool result = comparator(employees[ 0 ], employees[ 1 ]);

 

Итак, преобразуем программу к желаемому виду. Пусть выбор лямбда-выражения в зависимости от желаемого режима сортировки происходит в следующей функции:

 

std::function< bool (const Employee *, const Employee *) >

getEmployeeSortingPredicate (EmployeeSortMode _sortMode)

{

// Выбираем предикат для сортировки в зависимости от режима

switch (_sortMode)

{

// Сортировка по алфавиту

case EmployeeSortMode::Alphabetical:

return [] (const Employee * _pEmployee1, const Employee * _pEmployee2)

{

return _pEmployee1->getName() < _pEmployee2->getName();

};

 

// Сортировка по возрасту

case EmployeeSortMode::Age:

return [] (const Employee * _pEmployee1, const Employee * _pEmployee2)

{

return _pEmployee1->getAge() > _pEmployee2->getAge();

};

 

// Сортировка по зарплате

case EmployeeSortMode::Salaries:

return [] (const Employee * _pEmployee1, const Employee * _pEmployee2)

{

return _pEmployee1->getSalary() > _pEmployee2->getSalary();

};

 

// Неизвестный режим сортировки

default:

throw std::logic_error("Unexpected sort mode");

}

}

 

Основная функция сортировки заметно упрощается:

 

void sortEmployees (std::vector< Employee * > & _employees,
EmployeeSortMode _sortMode)

{

std::sort(

_employees.begin(),

_employees.end(),

getEmployeeSortingPredicate(_sortMode) // выбор предиката по режиму

);

}

 

Протестируем решение при помощи простой тестовой программы:

 

int main ()

{

// Набор тестовых данных о сотрудниках

std::vector< Employee * > employees;

employees.push_back(new Employee("Ivanov", 25, 1000.0));

employees.push_back(new Employee("Ivanov", 45, 2000.0));

employees.push_back(new Employee("Petrov", 50, 750.0));

employees.push_back(new Employee("Sidorov", 34, 1200.0));

 

// Сортировка по возрасту

std::cout << "===== By Age ===== " << std::endl;

sortEmployees(employees, EmployeeSortMode::Age);

printEmployees(employees);

 

// Сортировка по зарплате

std::cout << "===== By Salary ===== " << std::endl;

sortEmployees(employees, EmployeeSortMode::Salaries);

printEmployees(employees);

 

// Сортировка по алфавиту

std::cout << "===== By Name ===== " << std::endl;

sortEmployees(employees, EmployeeSortMode::Alphabetical);

printEmployees(employees);

 

// Освобождение объектов

std::for_each(
employees.begin(), employees.end(),
[] (Employee * pE) { delete pE; }
);

}

 

В результате запуска получим следующий результат:

 

 





Поделиться с друзьями:


Дата добавления: 2017-01-21; Мы поможем в написании ваших работ!; просмотров: 412 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Либо вы управляете вашим днем, либо день управляет вами. © Джим Рон
==> читать все изречения...

4330 - | 4046 -


© 2015-2026 lektsii.org - Контакты - Последнее добавление

Ген: 0.008 с.