Anuncios

Bienvenidos sean a este post, hoy veremos una tecnica interesante.

Anuncios

Sabemos que mediante condicionales podemos establecer un cierto razonamiento a nuestros codigos, de lo cual hablamos en este post. Y los punteros de funciones nos permiten establecer una direccion de memoria que podemos modificar para que apunte a diferentes funciones simplemente asignando una nueva, de lo cual hablamos en este post. Hoy veremos una tecnica interesante para poder reemplazar a uno con los otros, tal como indica el titulo. Para ello primero veamos el siguiente ejemplo:

#include <iostream>

int sumar(int a, int b) { return a + b; }
int restar(int a, int b) { return a - b; }
int multiplicar(int a, int b) { return a * b; }
int dividir(int a, int b) { return a / b; }

int main()
{
        char op;
        int num1, num2, final;
        std::cout << "Ingresa dos valores y la operacion: ";
        std::cin >> num1 >> num2 >> op;

        switch(op)
        {
                case '+':
                        final = sumar(num1, num2);
                        break;
                case '-':
                        final = restar(num1, num2);
                        break;
                case '*':
                        final = multiplicar(num1, num2);
                        break;
                case '/':
                        final = restar(num1, num2);
                        break;
                default:
                        std::cout << "Debes ingresar un operador valido.\n";
                        final = 0;
                        break;
        }

        std::cout << "Resultado: " << final << std::endl;

        return 0;
}
Anuncios
Anuncios

Primero definiremos una serie de funciones para realizar distintas operaciones. Cada una recibira dos valores y devolvera el resultado de la operacion realizada. En el main, declaramos una variable de tipo char que servira para recibir al operador y tres de tipo int para pasar los valores y almacenar el resultado devuelto por las funciones. Luego ingresaremos los valores, observen como lo pedimos. Debemos pasar los dos valores y el operador. Seguido a esto usaremos a switch para evaluar al operador. En cada caso, llamaremos a la funcion que corresponda y el resultado lo almacenaremos en final. Y tenemos a default donde lo usaremos si no pasamos un operador valido. En todos los casos establecemos un valor para final. Para finalmente mostrar dicho valor. Veamos como funciona:

$ ./punt
Ingresa dos valores y la operacion: 10 2 *
Resultado: 20
$
Anuncios

Como pueden ver trabaja perfectamente pero ahora tomaremos el codigo anterior y realizaremos una serie de modificaciones:

#include <iostream>
#include <map>

int sumar(int a, int b) { return a + b; }
int restar(int a, int b) { return a - b; }
int multiplicar(int a, int b) { return a * b; }
int dividir(int a, int b) { return a / b; }

int main()
{
        std::map<char, int (*)(int, int)> operaciones;
        operaciones['+'] = &sumar;
        operaciones['-'] = &restar;
        operaciones['*'] = &multiplicar;
        operaciones['/'] = &dividir;

        char op;
        int num1, num2, final;
        std::cout << "Ingresa dos valores y la operacion: ";
        std::cin >> num1 >> num2 >> op;
        final = operaciones[op](num1, num2);

        std::cout << "Resultado: " << final << std::endl;

        return 0;
}
Anuncios
Anuncios

Pocos cambios pero muy importantes. El primero fue agregar a la libreria map para poder crear objetos de este tipo. El segundo es en el main, donde ahora crearemos un objeto de tipo map. La clave sera de tipo char, porque nuestros operadores pasaran a ser las claves, y luego un puntero de funciones de tipo int que recibira dos valores. Si lo observan pueden darse cuenta de que coincide con la forma de las funciones de operaciones anteriores. Lo siguiente es la asignacion de cada operador como clave y a cada una de estas la asociaremos con la funcion que corresponda. Por ultimo, eliminamos el switch y agregamos el llamado a este objeto de tipo map, le pasamos como clave el operador informado y los valores. El resultado final lo asignaremos a final y lo mostraremos. Si lo compilan y ejecutan deben obtener el mismo resultado. Lo unico que perderemos es la opcion de informar cuando no pasamos un operador correcto pero resulta en un codigo mas simple y facil de manejar simplemente con un puntero de funciones eliminando la necesidad repetir codigo, siendo esta una de las premisas de cualquier lenguaje.

Anuncios

Dijimos que perdimos la posibilidad de notificar cuando un operador no es valido? Bueno, eso podemos solucionarlo. Para ello, modificaremos el codigo de la siguiente manera:

#include <iostream>
#include <map>
#include <functional>

int sumar(int a, int b) { return a + b; }
int restar(int a, int b) { return a - b; }
int multiplicar(int a, int b) { return a * b; }
int dividir(int a, int b) { return a / b; }

int main()
{
        std::map< char, std::function< int(int, int)> > operaciones;
        operaciones['+'] = &sumar;
        operaciones['-'] = &restar;
        operaciones['*'] = &multiplicar;
        operaciones['/'] = &dividir;

        char op;
        int num1, num2, final;
        std::cout << "Ingresa dos valores y la operacion: ";
        std::cin >> num1 >> num2 >> op;
        try {
                final = operaciones[op](num1, num2);
                std::cout << "Resultado: " << final << std::endl;
        } catch(std::bad_function_call e) {
                std::cout << "Ha ocurrido un error\n";
        }

        return 0;
}
Anuncios

Primero importaremos a la libreria functional. Esta nos permitira manejar objetos de tipo function. Esta pensado principalmente para manejar punteros de funciones pero podemos usarlo para otras finalidades. En nuestro ejemplo, lo usaremos para ello. Siguiendo con el ejemplo, en el objeto de tipo reemplazamos a:

int (*)(int, int)
Anuncios

Por lo siguiente:

std::function< int(int, int) >
Anuncios

Es basicamente lo mismo pero la diferencia radica que este nos devolvera una excepcion que podemos interceptar. Si se fijan, pasamos como tipo a la estructura de nuestras funciones. El tipo que devuelven y los valores que podemos pasar como argumentos. El resto de la creacion del objeto sigue siendo igual al anterior. Por ultimo, en el main rodeamos a la linea que llama al puntero y muestra el resultado con un try y el catch tiene una funcion que sera la excepcion que devolvera en caso de error. Si esto ocurre, mostraremos un mensaje indicando que la operacion es invalida. Compilemos y veamos como trabaja:

$ ./punt
Ingresa dos valores y la operacion: 10 2 *
Resultado: 20
$ ./punt
Ingresa dos valores y la operacion: 10 2 x
Ha ocurrido un error
$
Anuncios

Como pueden ver sigue funcionando correctamente pero ahora si pasamos mal al operador nos devolvera un error.

Anuncios

En resumen, hoy hemos visto una tecnica que podemos usar para reemplazar condicionales con punteros de funciones, puede tener su practicidad si necesitamos un codigo mas simple y limpio pero tambien vimos que perdimos algunas practicidades, aunque al final pudimos recuperarla con algunas herramientas adicionales. 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
pp258

Donatión

It’s for site maintenance, thanks!

$1.50