Ya vimos en este artículo que era posible guardar en una variable una REFERENCIA a una función. Pues, era de esperar, que también podemos pasar una función como parámetro a otra función.
El paso de referencias a funciones como parámetros para otra función, simplemente significa que la función “receptora” puede admitir REFERENCIAS a otras funciones como argumentos. (por supuesto también existe la posibilidad de devolver una función como return
).
Esto permite que la función receptora puede utilizar la función que recibe como parámetro. Ya sea invocándola directamente, almacenarla para su uso posterior, pasarla a si mismo a otra función. O incluso no hacer nada con ella. En definitiva, puede hacer con ella lo que quiera (como si fuera cualquier otra parámetro).
¿Por que alguien querría pasar una función a otra? Pues porque permite hacer cosas muy interesantes. Principalmente, nos permite escribir funciones más generales y reutilizables, ya que pueden trabajar con diferentes comportamientos especificados por la función referenciada.
En definitiva, que como mecanismo es la leche base para hacer herramientas potentes y reutilizable. Te costará hacerte un poco con ellas, pero compensa el esfuerzo. Así que vamos a ver su uso con un par de ejemplos.
Casos de usos de referencias a funciones
Un ejemplo, imagina que tienes una función que recorre los elementos de una colección. Además, la forma de recorrerla es compleja, así quieres te gustaría reaprovechar ese código lo máximo posible.
Ahora quieres que usar tu función para recorrer la colección y ejecutar una acción sobre los elementos que cumplan una criterio. Podemos reusar tu función recorre_coleccion
, si le pasamos también la función de criterio
, y la función action
a realizar.
function recorre_coleccion(criterio, action)
{
// hacer cosas
}
Otro ejemplo, imagina que una función tiene que hacer un proceso muy largo y complicado. Además, este proceso puede salir bien 👍, o dar un error ❌.
Puedes hacer una función haz_algo_muy_complicado
que haga el proceso. A esta le pasas dos funciones. Una de exito
que se ha ejecutado si todo sale bien, y una de error
si algo sale mal.
function haz_algo_muy_complicado(exito, error)
{
// haz algo muy complicado
// con posibles llamadas a error()
// y si todo sale bien, exito
exito();
}
¿Qué es lo que tienen estos dos ejemplos en común? Que pasar funciones como parámetros nos ha permitido reaprovechar el código de una función muy complicada. Le hemos añadido versatilidad, y la podemos usar en muchos más casos.
Ejemplos de funciones como parámetros en distintos lenguajes
Vamos a ver cómo se realizaría el uso de funciones de Orden Superior y paso de funciones como parámetros en distintos lenguajes de programación.
En este ejemplo, vamos a crear dos funciones Sumar
y Restar
, que reciben dos parámetros a
y b
y realizan la operación oportunda.
Por otro lado, vamos a tener una función de orden superior llamada EjecutarOperacion
. Esta función recibe dos parámetros a
y b
, y la operación a ejecutar.
Finalmente invocamos EjecutarOperacion
con distintos parámetros de a
y b
, y la operación a realizar (Sumar
o Restar
).
// Delegado para representar una función que toma dos enteros y devuelve un entero
delegate int Operacion(int a, int b);
// Función para sumar dos números
int Sumar(int a, int b)
{
return a + b;
}
// Función para restar dos números
int Restar(int a, int b)
{
return a - b;
}
// Función para ejecutar una operación matemática
int EjecutarOperacion(int a, int b, Operacion operacion)
{
return operacion(a, b);
}
Console.WriteLine(EjecutarOperacion(5, 3, Sumar)); // Imprime: 8
Console.WriteLine(EjecutarOperacion(5, 3, Restar)); // Imprime: 2
// Declaración de función de tipo puntero a función que toma dos enteros y devuelve un entero
typedef int (*Operacion)(int, int);
// Función para sumar dos números
int Sumar(int a, int b)
{
return a + b;
}
// Función para restar dos números
int Restar(int a, int b)
{
return a - b;
}
// Función para ejecutar una operación matemática
int EjecutarOperacion(int a, int b, Operacion operacion)
{
return operacion(a, b);
}
int main()
{
// Utilizar referencias a funciones para realizar operaciones
std::cout << EjecutarOperacion(5, 3, Sumar) << std::endl; // Imprime: 8
std::cout << EjecutarOperacion(5, 3, Restar) << std::endl; // Imprime: 2
return 0;
}
// Función para sumar dos números
function sumar(a, b) {
return a + b;
}
// Función para restar dos números
function restar(a, b) {
return a - b;
}
// Función para ejecutar una operación matemática
function ejecutarOperacion(a, b, operacion) {
return operacion(a, b);
}
// Utilizar referencias a funciones para realizar operaciones
console.log(ejecutarOperacion(5, 3, sumar)); // Imprime: 8
console.log(ejecutarOperacion(5, 3, restar)); // Imprime: 2
# Función para multiplicar dos números
def multiplicar(a, b):
return a * b
# Función para dividir dos números
def dividir(a, b):
return a / b
# Función para ejecutar una operación matemática
def ejecutar_operacion(a, b, operacion):
return operacion(a, b)
# Utilizar referencias a funciones para realizar operaciones
print(ejecutar_operacion(5, 3, multiplicar)) # Imprime: 15
print(ejecutar_operacion(5, 3, dividir)) # Imprime: 1.6666666666666667
Funciones de orden superior
Las funciones que aceptan otras funciones como argumentos o devuelven funciones como resultados se conocen como funciones de orden superior.
Es decir, una Función de Orden Superior es aquella que,
- Puede recibir una o más funciones como argumentos
- Puede devolver una función como resultado (con
return
).
Es un término que se ha hecho popular, porque tiene un papel (sobre todo teórico) importante en la programación funcional. Y la programación funcional “está de moda”.
// funcion "super guay" que recibe otra funcion
function FuncionDeOrdenSuperior(function otraFuncion)
{
}
// pobre funcion plebleya que solo recibe numeros
function FuncionNormal(int numero)
{
}
Pero tampoco le deis importancia de más. En en el fondo, no son más que funciones normal y corrientes, igual que las demás.
Simplemente que alguno de los tipos de datos que maneja son Referencias a funciones. (pero hay un pijerio impresionante con ponerle nombre a todo 😄).
Relación con funciones lambda
Las funciones lambda (también conocidas como funciones anónimas), son una forma concisa de definir funciones en línea sin necesidad de asignarles un nombre.
Las funciones lambda son especialmente útiles cuando se necesita una función que sólo vas a usar una vez, sobre todo si estas es corta. Aquí no te compensa crear una función “normal”, y las funciones lambdas te facilitan la vida.
Así que por un lado tenemos una sintaxis de funciones cortas, y por otro funciones que aceptan otras funciones. Veis por donde voy ¿no?. Son conceptos que encajan muy bien.
function ExecuteAction(action)
{
// hacer lo que sea
}
ExecuteAction(() => console.log("hola")); // mira que comodo 😉
La combinación de funciones lambda y el paso de referencias a funciones como parámetros es una práctica común y muy habitual en muchos lenguajes de programación modernos.
Funcionamiento interno Avanzado
El funcionamiento interno del paso y devolución de funciones desde una función es muy sencillo, si has entendido el concepto de una REFERENCIA a una función.
Recordemos que una referencia a una función es simplemente una variable que contiene la dirección de una función, en vez de un dato, o la dirección de otra variable.
Aparte de eso, para la función receptora la gestión es exactamente igual que con cualquier otro tipo de variable. La recibe, la devuelve, la copia, la guarda… a la función le da básicamente igual.
// a esta funcion le da igual que algo sea 10, "patata" o una funcion
function FuncionDeOrdenASaber(algo)
{
}
La diferencia será en el uso que se haga de la variable. Y, si el lenguaje tiene tipado, de la comprobación que haga de que los tipos son correctos.
function FuncionDeOrdenASaber(algo)
{
algo(); // aqui esta la diferencia, al usar la variable
}
// aqui hay otra diferencia, al pasar la variable
FuncionDeOrdenASaber(() => console.log("hola"));
Pero en realidad, no hay ninguna diferencia en cuanto a funcionamiento de una Referencia a una función, que con cualquier otro tipo de variable.