Anuncios

Bienvenidos sean a este post, hoy veremos otro tipo de funciones.

Anuncios

En la programacion funcional, las funciones se consideran como objetos de primera clase. Esto implica que las mismas deben ser tratadas como objetos en lugar de un conjunto de instrucciones. Esto implica que al ser tratado como un objeto podemos pasarlo a otras funciones. Esto es lo que habitualmente se denomina como funciones de orden superior. Veamos como se realiza esto mediante un ejemplo de la vieja escuela:

typedef  void (*PF)(int);
void foo(int arg) 
{
  // procesamos a arg
}

int bar(int arg, PF f)
{
  f(arg);
  return arg;
}

bar(42, foo);
Anuncios
Anuncios

Primero declaramos un puntero de funcion, puede recibir valores de tipo int, y en este caso no devuelve ninguno. Luego tenemos una funcion que recibe un argumento de tipo int y lo procesa. La siguiente funcion recibe dos argumentos, siendo uno de tipo int y el otro sera el puntero de funciones definido al inicio. Ejecuta la funcion recibida y le pasa como argumento el otro recibido. Por ultimo, hacemos un llamado a la segunda funcion pasando un valor y la primera funcion. Esto es lo clasico, como dijimos anteriormente, y sobre esto hablamos en este post.

Anuncios

Hasta ahora cuando definimos un objeto es como algo con un estado. Esto implica que si necesitamos tratar una funcion como a un objeto, tambien necesitamos de alguna forma cambiar su estado. Siendo que este no es el caso para los punteros de funciones. Veamos otro enfoque de como pasar una funcion a otra funcion:

class Funcion
{
public:
  void modificar_estado(int a) {
    estado = a;
  }

  int get_estado() {
    return estado;
  }

  void operator()() {
    // intrucciones que ejecuta la "funcion"
  }
private:
  int estado;
};

void foo(Funcion f)
{
  f();
  // y aqui tendremos otro codigo util.
}
Anuncios
Anuncios

Aqui lo hacemos casi literalmente, porque definimos una clase que se llama Funcion, en la parte privada tiene una propiedad para controlar el estado y en la publica tenemos dos metodos para modificar y recuperar la propiedad anterior. Y tambien una sobrecarga del operador de funcion donde cada vez que sea usado ejecutaremos las acciones correspondientes. En la funcion foo tenemos un argumento de la clase anterior y ese objeto lo ejecutamos como una funcion en el bloque, gracias a la sobrecarga, junto a otras instrucciones. Veamos como podemos trabajar con foo:

void foo(Funcion f)
{
  f();
  f.modificar_estado(11);
  std::cout << f.get_estado(); // consigue el estado
  f(); // llama a la "funcion"
}
Anuncios

Primero ejecutamos la funcion de nuestro objeto, modificamos el estado y luego mostramos el nuevo estado de la funcion, y podemos llamarlo nuevamente. Esto tambien nos permite otras opciones, como por ejemplo hacer un conteo de la cantidad de veces que es llamada, etc.

Anuncios

Pero una de las grandes ventajas de este lenguaje, es que no se debe reinventar la polvora. Por eso, tenemos una libreria llamada functional y entre ellas tiene una clase llamada function, la cual nos permite realizar lo mismo. Vamos a analizar el siguiente ejemplo:

#include <iostream>
#include <functional>

std::function<int (int, int)> sumador()
{
        return [](int a, int b){ return a + b; };
}

int main()
{
        auto sumar = sumador();
        std::cout << sumar(10,15) << std::endl;

        return 0;
}
Anuncios
Anuncios

Veamos como es la clase para la funcion, en ella aplicamos la clase function y observen como pasamos los tipos. El primero sera para indicar el tipo que devuelve y los tipos de los dos argumentos que recibira, y luego el identificador de la funcion. En esta devolveremos la suma de los dos valores recibidos. Tal como si fuera una funcion anonima de las que vimos cuando hablamos de ranges en este post. En el main, creamos una variable y le asignaremos la funcion anterior, para luego llamarla, le pasamos dos valores y mostramos el resultado. Compilemos y veamos la salida:

$ ./func
25
$
Anuncios

Realizo la accion que solicitamos y la funcion fue manipulada desde una clase pensada para eso. Pero tambien podemos manipularlo de otra manera, tomemos el codigo anterior y hagamos el siguiente cambio:

#include <iostream>
#include <functional>

std::function<int (int)> sumador(int a)
{
        return [a](int b){ return a + b; };
}

int main()
{
        std::cout << sumador(10)(15) << std::endl;

        return 0;
}
Anuncios
Anuncios

Pequeños cambios pero que pueden ser utiles dependiendo de la situacion. El primero es en la declaracion de la funcion, donde solo recibe un argumento y el otro lo hara en el identificador de la misma. Otro cambio es en la funcion anonima, donde ahora entre los corchetes pasaremos el valor recibido en la funcion y luego esta el otro de la clase. Las ultimas modificaciones la hicimos en el main donde primero eliminamos a la variable con la funcion y despues la llamamos directamente pero le pasamos los valores entre parentesis distintos para cada seccion de la funcion. Si lo compilan y ejecutan deben obtener la misma salida. Otra cosa que podemos hacer es lo siguiente:

#include <iostream>
#include <functional>

std::function<int (int)> sumador(int a)
{
        return [a](int b){ return a + b; };
}

int main()
{
        auto sumarA20 = sumador(20);
        std::cout << sumador(10)(15) << std::endl;
        std::cout << sumarA20(10) << std::endl;

        return 0;
}
Anuncios

Simplemente agregamos una variable y en este asignamos la nueva funcion pero con un valor predeterminado. En este caso es para hacer lo que la identifica, que el valor que le pasemos lo sume a 20. Luego mostramos el llamado a esta variable pero solo pasamos un valor porque a diferencia del otro llamado, el valor predeterminado compensa el parentesis faltante. Si lo compilan y ejecutan deben tener la siguiente salida:

$ ./func
25
30
$ 
Anuncios

El valor de la primera llamada a la funcion, y el nuevo valor de la llamada a la nueva funcion. Si lo observan bien, cuando trabajamos con ranges, transform y filter, al momento de pasar la funcion es de este tipo.

Anuncios

En resumen, hoy hemos visto a funciones de orden superior, que son, para que sirven, como se utilizan, el concepto clasico mediante el puntero de funciones, y luego una mejor version mediante la libreria functional. Espero les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Donation

It’s for maintenance of the site, thanks!

$1.50