Functores y ordenación mediante sort en la STL
La STL, o Standard Template Library, es un conjunto de plantillas para C++ que facilitan la vida al programador, y además de una forma eficiente.
Para quién no lo conozca, un Functor o Function Object es un objeto que puede ser invocado como una función. Por tanto, nos estamos refiriendo a la instancia de a una clase o a una estructura, la cuál tiene definido el operator().
¿Cómo definir un Functor? Lo normal es hacerlo mediante estructuras, ya que suelen ser muy específicos (aunque nada ni nadie impide que se usen clases). Vamos a implementar un Functor que compare flotantes y nos indique si uno es mayor que otro:
struct MayorFlotante
{
bool operator()(double x, double y)
{
return x > y;
}
};
Seguramente estarás pensando, "¡Vaya tontería! Eso es un simple if". Verás como no es tanta tontería.
Continuamos, ¿este struct es entonces nuestro Functor? Realmente no. Como ya dije, un Functor es una instancia, por ejemplo:
MayorFlotante comp_mayor;
comp_mayor(3.5, 8.2); // <-- Devuelve false
Definimos una instancia (una variable, vamos) llamada comp_mayor, y a partir de ahí podremos hacer uso del Functor.
Imagina que ahora queremos comparar el mayor en valor absoluto:
struct MayorFlotanteAbsoluto
{
bool operator()(double x, double y)
{
return fabs(x) > fabs(y);
}
};
Pero, ¿para qué sirve todo esto? Supongamos un vector de la STL que queremos ordenar. La STL ofrece un método sort (dentro del apartado algorithm) que nos permitirá ordenar los elementos del vector. Un ejemplo:
#include <algorithm>
#include <vector>
int main()
{
vector<float> v;
...
sort(v.begin(), v.end());
}
El método sort recibe dos iteradores de acceso aleatorio, esto es, dos iteradores de estructuras que permitan acceso aleatorio a los elementos como son el vector y deque. Por defecto, sort utiliza el operator< para realizar la ordenación. Con los tipos de datos básicos no hay problema, pero si tenemos una clase propia, estamos obligados a implementar dicho operador. Por ejemplo:
class MiClase
{
public:
bool operator<(const MiClase &); // <-- Sin este método no es posible usar sort
...
};
int main()
{
vector<MiClase> v;
...
sort(v.begin(), v.end());
}
Existe otra forma de utilizar sort y es haciendo uso de un Functor. Volviendo con nuestro ejemplo del Functor MayorFlotante. Supongamos que queremos ordenar el vector de mayor a menor:
int main()
{
MayorFlotante comp;
vector<float> v;
...
sort(v.begin(), v.end(), comp);
}
Así se consigue independizar el algoritmo de ordenación del criterio que usemos para ordenar. Veamos un ejemplo completo.
Tenemos información sobre una serie de puntos 2D (x, y) en un vector y queremos crear dos vectores más, uno en el que los puntos estén ordenados por x y otro en el que los puntos estén ordenados por y. Para la representación del punto vamos a utilizar un pair (también de la STL).
#include <utility>
#include <algorithm>
#include <vector>
struct MenorX
{
bool operator()(pair<float,float> p1, pair<float,float> p2)
{
return p1.first < p2.first;
}
};
struct MenorY
{
bool operator()(pair<float,float> p1, pair<float,float> p2)
{
return p1.second < p2.second;
}
};
int main()
{
vector< pair<float,float> > puntos;
...
MenorX comp_x;
MenorY comp_y;
vector< pair<float,float> > x(puntos); // Hacemos la copia de puntos
vector< pair<float,float> > y(puntos); // Y hacemos otra copia
sort(x.begin(), x.end(), comp_x);
sort(y.begin(), y.end(), comp_y);
}
Tags
La teoría es cuando crees saber algo, pero no funciona.
La práctica es cuando algo funciona, pero no sabes por qué.
Los programadores combinan la teoría y la práctica:
Nada funciona y no saben por qué.
