programacion-funciones-lambda

Qué son y cómo usar funciones lambda

Las funciones lambda (también conocidas como arrow functions) son una sintaxis para declarar funciones anónimas de forma concisa y expresiva.

Una función anónima es aquella que definimos sin un nombre específico. Es decir, a diferencia de las funciones regulares, no se definen con una declaración completa y no requieren un identificador.

La principal ventaja de las funciones lambda es su capacidad para ser creadas y utilizadas de forma inmediata (esto es muy útil, por ejemplo, si es una función que vamos a usar una única vez).

Por ejemplo, aquí tenemos una función myfunc que simplemente suma dos números, y su equivalente en función lambda.

function myfunc(a, b) {
 return a + b
}

(a, b) => a + b

Como vemos, la función lambda es una forma más concisa de definir la función, y que no requiere que le demos un nombre (es anónima).

Esto mejora la legibilidad del código y facilita su comprensión. Las funciones lambda son una herramienta muy útil en programación, y fundamentales en la programación funcional

Ejemplos de funciones lambda en distintos lenguajes

La sintaxis general de una función lambda varía según el lenguaje de programación, pero sigue un patrón común. Veamos un ejemplo de la estructura básica de una función lambda en distintos lenguajes:

(parameter_list) => { statements }
[capture_list] (parameter_list) { statements }
(parameter_list) => { statements }
lambda parameter_list: statements

Utilizando funciones lambda

Las funciones lambda son especialmente útiles cuando requiere una funcionalidad simple, y definir una función completa sería un gasto de caracteres innecesario.

Se pueden utilizar en una variedad de situaciones, como por ejemplo,

Funciones de orden superior

Las funciones lambda pueden ser utilizadas como argumentos en funciones de orden superior (como map, filter y reduce) lo que permite una programación más funcional y expresiva.

Una función es de orden superior si toma una función como argumento o devuelve una función como resultado.

En este caso, la función lambda se utiliza como argumento de map, aplicando la operación de elevar al cuadrado a cada elemento de la lista numbers.

Veamos un ejemplo utilizando Python:

personas_mayores_de_20 = people.Where(person => person.edad > 20);
const personasMayoresDe20 = people.filter(person => person.edad > 20);
personas_mayores_de_20 = filter(lambda person: person.edad > 20, people)

Callbacks y eventos

Otro uso frecuente de funciones lambda es ser utilizadas como callbacks o manejadores de eventos. Aquí hay un ejemplo sencillo en JavaScript que muestra cómo utilizar una función lambda como manejador de clics en un botón:

const button = document.getElementById('myButton');

button.addEventListener('click', 
                         () => { console.log('¡Hiciste clic en el botón!');}
                       );

Closures

Una closure (también conocida como clausura o cierre) es un concepto que se refiere a una función junto con el entorno léxico en el que fue creada. Esto significa que la función “recuerda” las variables que estaban disponibles en el momento de su creación.

En otras palabras, una closure permite que una función conserve el estado de las variables locales existentes cuando fue definida (incluso después de que la función haya terminado su ejecución=.

En muchos lenguajes de programación que admiten funciones lambda o arrow functions, como JavaScript o Python, una closure se crea automáticamente cuando una función lambda se define dentro de otra función.

Supongamos que estamos trabajando en JavaScript:

function myFunction() {
  let x = 10;

  // Definimos una función lambda (arrow function) dentro de outerFunction
  const innerFunction = () => {
    console.log(x); // La función lambda utiliza la variable 'x' de outerFunction
  };
}

En este ejemplo, innerFunction es una función lambda definida dentro de outerFunction. La función innerFunction puede acceder a la variable x, incluso aunque esta existe fuera de su ámbito.

De hecho, las closures porque “recuerdan” el valor de la variable x que estaba disponible en el ámbito de outerFunction en el momento en que se creó.

Aunque myFunction ya ha terminado su ejecución y la variable x no es accesible desde fuera de myFunction, la closure innerFunction aún tiene acceso a x.

Cuando invocamos myFunction, esta función interna se ejecutará y mostrará el valor de x, que es 10, porque conserva la referencia al entorno léxico de myFunction.

function outerFunction() {
  let x = 10;

  const innerFunction = () => {
     console.log(x);
  };

  return innerFunction;
}

const closureFunc = outerFunction(); // outerFunction ha terminado y devuelto innerFunction
closureFunc(); // Aquí se invoca innerFunction, que sigue teniendo acceso a x

No en todos los lenguajes las capturas y closures son automáticas. Por ejemplo, en C++ hay que especificar los valores que son capturados

#include <iostream>
#include <functional>

std::function<void()> outerFunction() {
    int x = 10;

    // Definimos una lambda que captura la variable 'x' por valor
    auto innerFunction = [x]() {
        std::cout << x << std::endl; // La lambda utiliza la variable 'x' de outerFunction
    };

    return innerFunction;
}

int main() {
    auto closureFunc = outerFunction();
    closureFunc(); // Aquí se invoca la función interna (innerFunction)

    return 0;
}