Componibilidad de algoritmos STL


Los algoritmos STL son algo bastante útil en C++. Pero una cosa que me molesta es que parecen carecer de composición.

Por ejemplo, digamos que tengo un vector<pair<int, int>> y quiero transformarlo en un vector<int> que contiene solo el miembro second del par. Eso es bastante simple:

std::vector<std::pair<int, int>> values = GetValues();
std::vector<int> result;

std::transform(values.begin(), values.end(), std::back_inserter(result),
    [] (std::pair<int, int> p) { return p.second; });

O tal vez quiero filtrar el vector solo para aquellos pares cuyo miembro first es par. También bastante simple:

std::vector<std::pair<int, int>> values = GetValues();
std::vector<std::pair<int, int>> result;

std::copy_if(values.begin(), values.end(), std::back_inserter(result),
    [] (std::pair<int, int> p) { return (p.first % 2) == 0; });

Pero ¿qué pasa si quiero hacer ambas cosas? No hay transform_if algoritmo, y el uso de transform y copy_if parece requerir la asignación de un vector temporal para mantener el resultado intermedio:

std::vector<std::pair<int, int>> values = GetValues();
std::vector<std::pair<int, int>> temp;
std::vector<int> result;

std::copy_if(values.begin(), values.end(), std::back_inserter(temp),
    [] (std::pair<int, int> p) { return (p.first % 2) == 0; });

std::transform(values.begin(), values.end(), std::back_inserter(result),
    [] (std::pair<int, int> p) { return p.second; });

Esto me parece bastante derrochador. La única manera que se me ocurre para evitar el vector temporal es abandonar transform y copy_if y simplemente usar for_each (o un bucle for regular, lo que se adapte a su fantasía):

std::vector<std::pair<int, int>> values = GetValues();
std::vector<int> result;

std::for_each(values.begin(), values.end(),
    [&result] (std::pair<int, int> p) 
        { if( (p.first % 2) == 0 ) result.push_back(p.second); });

¿Me estoy perdiendo algo aquí? ¿Hay una buena manera de componer dos algoritmos STL existentes en uno nuevo sin necesidad de temporal de almacenamiento?

Author: Sven, 2011-07-19

3 answers

Tienes razón. Puedes usar Boost.Adaptadores de gama para lograr la composición.

 23
Author: ybungalobill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-19 06:37:20

Creo que el problema es desafortunadamente estructural

  1. C++ utiliza dos iteradores para representar una secuencia
  2. Las funciones de C++ son de un solo valor

Así que no puede encadenarlos porque una función no puede devolver "una secuencia".

Una opción habría sido usar secuencias de objeto único en su lugar (como el enfoque de rango de boost). De esta manera podría haber combinado el resultado de un procesamiento como la entrada de otro... (un objeto -> uno objeto).

En la biblioteca estándar de C++, el procesamiento es (dos objetos -> un objeto) y está claro que esto no se puede encadenar sin nombrar el objeto temporal.

 10
Author: 6502,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-19 06:53:50

En el año 2000, el problema ya se notó. A Gary Powell y Martin Weiser se les ocurrió un concepto de "vista", y acuñaron el nombre de "Biblioteca de plantillas de vista". No despegó entonces, pero la idea tiene sentido. Un adaptador" view " esencialmente aplica una transformación sobre la marcha. Por ejemplo, puede adaptar el value_type.

El concepto probablemente debería ser readdressed ahora que tenemos C++0x. Hemos hecho bastante progreso en la programación genérica desde 2000.

Por ejemplo, usemos el vector<pair<int, int>> para vector<int> ejemplo. Eso podría ser bastante simple:

std::vector<std::pair<int, int>> values = GetValues();
vtl2::view v (values, [](std::pair<int, int> p) { return p.first }); 
std::vector<int> result(view.begin(), view.end());

O, usando las técnicas boost::bind, aún más simple:

std::vector<std::pair<int, int>> values = GetValues();
vtl2::view v (values, &std::pair<int, int>::first); 
std::vector<int> result(view.begin(), view.end());
 8
Author: MSalters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-19 08:13:20