30 ноября 2010

C++0x: lambda functions

Наверное, около месяца назад установил на домашней машине gcc версии 4.5. Эта версия в достаточно полном объеме поддерживает стандарт C++0x, который, в частности, позволяет объявлять в программе анонимные функции и, более того, захватывать текущие значения переменных (контекст) в тело функции. В стандарте эта возможность названа lambda functions, а, в принципе, захват контекста при создании функции обычно называют лексическим замыканием. Самый простой пример такого замыкания выглядит следующим образом.

#include <iostream>

int main() {
auto a = 2;
auto twice = [a] (int x) { return a * x; };
std::cout << "2x2=" << twice(2) << std::endl;
a = 3;
std::cout << "2x2=" << twice(2) << std::endl;
return 0;
}


Здесь мы создаем анонимную функцию и присваиваем ее переменной twice. auto означает, что компилятору предлагается самому вывести тип переменной на основании инициализатора. В первом случае, тип a будет int (сделано исключительно ради иллюстрации), а во втором — lambda(int). Синтаксис объявления lambda function следующий: [список переменных для захвата] (список параметров функции) {тело функции}. При необходимости, можно добавить еще и тип возвращаемого значения (подробнее — хотя бы здесь). Вообще говоря, подобный синтаксис настораживает. Он позволяет писать в коде что-нибудь вроде [=](){}, и это будет нормально компилироваться. C++ от этого явно читабельнее не станет:)
Итак, при создании анонимной функции указано, что значение переменной a необходимо сохранить непосредственно в теле функции. Теперь, даже после модификации этой переменной в глобальном контексте, функция все равно будет умножать входной параметр на 2. В результате, программа выведет дважды «2x2=4». (Компилировать надо как-то так g++-4.5 -std=c++0x test-lambda.cpp)
Следующий пример демонстрирует случай, когда без замыкания не обойтись. Пусть мы хотим заполнить матрицу целыми случайными значениями так, чтобы в каждой строке любое из них не превышало номер строки. В данном случае, для заполнения напрашивается функция generate из стандартной библиотеки. В качестве последнего параметра мы можем передать анонимную функцию, которая будет генерировать случайные числа. Но тут возникает вопрос: как передать в функцию номер строки матрицы, если ее будет вызывать generate? Ответ прост, номер можно поместить в функцию до ее передачи, захватив значение из контекста.

#include <iostream>
#include <algorithm>
#include <iterator>

using namespace std;

int main() {
const int N = 10;

int a[N][N];
for (int i = 0; i < N; ++i) {
generate(a[i], a[i] + N, [=] () { return rand() % (i + 1); });
copy(a[i], a[i] + N, ostream_iterator<int> (cout, " "));
cout << endl;
}
return 0;
}


Результат работы будет следующим:

0 0 0 0 0 0 0 0 0 0
0 1 0 1 1 0 0 0 0 0
2 2 0 0 2 2 2 1 1 1
1 2 2 2 1 3 1 0 3 2
4 3 1 4 4 2 3 4 0 0
5 4 1 2 2 3 2 2 4 1
2 6 4 5 3 5 5 4 3 0
4 7 6 1 6 7 2 4 3 6
7 6 6 8 5 3 6 2 8 1
2 0 6 8 9 2 6 6 4 9

1 комментарий:

Aberro комментирует...

Спасибо) Полезно и информативно)