Translate

martes, 27 de agosto de 2024

Lambdas en C++


El lenguaje C++ es conocido por su potencia y flexibilidad, y una de las características que refuerza esta reputación es la introducción de las expresiones lambda. Estas funciones anónimas y ligeras permiten escribir código más limpio y conciso, especialmente en escenarios donde se necesitan funciones pequeñas y de un solo uso. 

Una lambda es una función anónima que se puede definir en línea en el lugar donde se utiliza. Las lambdas permiten crear pequeñas funciones sin necesidad de nombrarlas o declararlas previamente. Fueron introducidas en C++11 y han sido una herramienta clave para el desarrollo moderno en C++.

La sintaxis básica de una lambda en C++ es :


[captura](parametros) -> tipo_retorno {

    // cuerpo de la lambda

};



  • [captura]: Define cómo la lambda captura las variables del entorno en el que se declara.
  • (parametros): Lista de parámetros que la lambda acepta.
  • -> tipo_retorno: Especifica el tipo de retorno de la lambda (puede omitirse si el compilador puede inferirlo).
  • { cuerpo }: El código que define la función de la lambda.


Veamos un ejemplo simple de una lambda que suma dos números:


#include <iostream>


int main() {

    auto suma = [](int a, int b) -> int {

        return a + b;

    };


    std::cout << "La suma de 3 y 4 es: " << suma(3, 4) << std::endl;

    return 0;

}


En este ejemplo, la lambda captura dos parámetros `a` y `b`, y retorna su suma.

Una de las características más poderosas de las lambdas es su capacidad para capturar variables del entorno donde son definidas. Hay varias formas de hacerlo:

  • Captura por valor: `[x]` captura `x` por valor.
  • Captura por referencia: `[&x]` captura `x` por referencia.
  • Captura todo por valor: `[=]` captura todas las variables que se usan en la lambda por valor.
  • Captura todo por referencia: `[&]` captura todas las variables que se usan en la lambda por referencia.


#include <iostream>


int main() {

    int x = 10;

    int y = 20;


    auto suma = [x, &y]() {

        y = x + y;

    };


    suma();

    std::cout << "El nuevo valor de y es: " << y << std::endl;  // Imprime 30

    return 0;

}


Aquí, `x` se captura por valor, y `y` se captura por referencia, lo que significa que cualquier modificación de `y` dentro de la lambda afecta a `y` fuera de la lambda.

Las lambdas son particularmente útiles cuando se combinan con las funciones de la STL (Standard Template Library) como `std::sort`, `std::for_each`, etc.

Por ejemplo, ordenar un vector de enteros en orden descendente usando `std::sort` y una lambda:


#include <iostream>

#include <vector>

#include <algorithm>


int main() {

    std::vector<int> vec = {3, 1, 4, 1, 5, 9};


    std::sort(vec.begin(), vec.end(), [](int a, int b) {

        return a > b;  // Orden descendente

    });


    for (int n : vec) {

        std::cout << n << " ";  // Imprime: 9 5 4 3 1 1

    }

    return 0;

}


Por defecto, las variables capturadas por valor dentro de una lambda no pueden ser modificadas. Sin embargo, si necesitas modificar las variables capturadas por valor, puedes declarar la lambda como `mutable`:


#include <iostream>


int main() {

    int x = 10;


    auto incrementa = [x]() mutable {

        x++;

        std::cout << "Valor dentro de la lambda: " << x << std::endl;

    };


    incrementa();  // Imprime 11

    std::cout << "Valor fuera de la lambda: " << x << std::endl;  // Imprime 10


    return 0;

}


En este caso, `x` se incrementa dentro de la lambda, pero fuera de ella permanece inalterado.

Las lambdas en C++ son una herramienta poderosa para escribir código más claro y conciso. Facilitan la escritura de funciones pequeñas y de un solo uso y son especialmente útiles cuando se trabaja con funciones de la STL y otras APIs que aceptan funciones como parámetros.