Skip to Content

(Opcional) C++ — expresiones lambda

Introducción

Recursos: Preguntas frecuentes
FuenteRecursoNotas
SOType of a lambda function using auto?
SOWhat is meant by 'retain state' in c++?

¿Por qué usar lambdas?

Las lambdas permiten escribir funciones simples y anónimas en línea. Así se pueden escribir funciones más chicas manteniendo el código local y organizado. Además, las lambdas pueden capturar las variables del entorno, lo que suele ser conveniente al escribir funciones auxiliares.

La biblioteca estándar de C++ tiene buen soporte para lambdas, con funciones como std::sort y std::lower_bound que permiten pasar funciones personalizadas para actuar como comparador de objetos. Suele ser más conveniente que la alternativa de escribir una función dedicada como comparador.

Forma general

Las lambdas tienen la siguiente forma:

[capture_list](parameters) -> trailing_return_type { // function body }

Los parámetros, el tipo de retorno y el cuerpo de la función son bastante directos. En programación competitiva, en general se usan estos tipos de captura:

[]: No captura nada del alcance local.

[&]: Captura todo el alcance local por referencia.

[=]: Captura todo el alcance local por copia.

El alcance local de la lambda es el alcance donde se define, no el alcance donde se usa.

También se puede especificar qué variables capturar, pero normalmente no es necesario.

Como ejemplo de lambda, supongamos que se quiere escribir una función que devuelva el cuadrado de un número dado. Se puede escribir la siguiente expresión lambda:

auto square = [](int x) -> long long { return (long long)x * x; };

Después se puede llamar a la lambda como una función normal.

cout << square(10) << '\n'; // prints out 100

Como se menciona en el módulo de comparadores personalizados, las lambdas también se pueden usar como comparadores.

Lambdas recursivas

Supongamos que se quiere escribir una función recursiva de GCD. Con una lambda, la forma más directa de escribirla sería así:

auto gcd = [](int a, int b) -> int { return b == 0 ? a : gcd(b, a % b); };

Por desgracia, esto no funciona, porque una lambda no puede referenciarse directamente a sí misma en su definición. Hay, no obstante, formas de evitarlo.

Con y_combinator

Recursos
FuenteRecursoNotas
open-stdY Combinator Proposal
RIP TutorialRecursive Lambdas

Si se agrega lo siguiente del enlace de arriba en C++14:

namespace std { template <class Fun> class y_combinator_result { Fun fun_; public: template <class T> explicit y_combinator_result(T &&fun) : fun_(std::forward<T>(fun)) {} template <class... Args> decltype(auto) operator()(Args &&...args) { return fun_(std::ref(*this), std::forward<Args>(args)...); } }; template <class Fun> decltype(auto) y_combinator(Fun &&fun) { return y_combinator_result<std::decay_t<Fun>>(std::forward<Fun>(fun)); } } // namespace std

Entonces se puede escribir código como el siguiente:

int main() { cout << y_combinator([](auto gcd, int a, int b) -> int { return b == 0 ? a : gcd(b, a % b); })(20, 30) << "\n"; // outputs 10 }

Con std::function

En lugar de auto, usar function<return_type(param)>.

int main() { function<int(int, int)> gcd = [&](int a, int b) { return b == 0 ? a : gcd(b, a % b); }; cout << gcd(20, 30) << '\n'; // outputs 10 }

Con lambdas genéricas

Para resolver el problema de que la lambda no puede acceder a sí misma, se pasa a sí misma.

int main() { auto gcd = [&](int a, int b, auto &&gcd) -> int { return b == 0 ? a : gcd(b, a % b, gcd); }; cout << gcd(20, 30, gcd) << '\n'; // outputs 10 }