2009-09-30 23 views
6

Estaba deambulando cómo es posible reverese string s que están contenidas en un vector usando un solo comando for_each solo en una línea "simple".Invirtiendo cadenas en un vector usando for_each y bind

Sí, sé que es fácil con un functor personalizado, pero no puedo aceptar que no se puede hacer usando bind (al menos no pude hacerlo).

#include <vector> 
#include <string> 
#include <algorithm> 

std::vector<std::string> v; 
v.push_back("abc"); 
v.push_back("12345"); 

std::for_each(v.begin(), v.end(), /*call std::reverse for each element*/); 

Editar: Gracias mucho por esas soluciones funtastic. Sin embargo, la solución para mí no era usar el tr1::bind que viene con el paquete de características/SP1 de Visual Studio 2008. No sé por qué no funciona como se esperaba, pero así son las cosas (even MS admits that it's buggy). Tal vez algunos hotfixes ayudarán.

Con boost :: bind todo funciona como se desea y es tan fácil (pero a veces relly messy :)). Realmente debería haber intentado impulso :: unen en el primer lugar ...

+3

"Sí, ya sé que es fácil de hacer con una llave, pero no puedo aceptar que no se puede hacer usando el martillo" –

+1

@R. Pate: me parece que saber cómo usar todas las herramientas en mi caja de herramientas en muchas situaciones, incluso donde otra herramienta hubiera sido mejor, es muy importante para mejorar la experiencia. Mi código de mascota está lleno de esto. En el código de producción, generalmente trato de elegir el mejor disponible (que sé, porque he experimentado con todos). –

+0

También me gustaría subrayar que el uso de bind/lambdas es más claro: la definición de lo que se hace es local, lo que ayuda a las personas que lo leen ... si no es demasiado desordenado. –

Respuesta

13

std :: for_each espera una función unaria (o al menos algo con los typedefs de una función unaria).

std :: reverse <> es una función binaria. Se necesitan dos iteradores. Sería posible unirlo todo usando boost :: bind, pero sería un lío bastante horrible. Algo así como:

boost::bind(
    &std::reverse<std::string::iterator>, 
     boost::bind(&std::string::begin, _1), 
     boost::bind(&std::string::end, _1)) 

mejor, creo, sería escribir una función llamada reutilizable reverse_range así:

template <class Range> 
void reverse_range(Range& range) 
{ 
    std::reverse(range.begin(), range.end()); 
} 

(probablemente con alguna metaprogramming para asegurar que la gama & no es un doble referencia)

Y luego usar eso en su for_each (después de adaptarlo para ser una función única, por supuesto).

std::for_each(v.begin(), v.end(), 
    std::ptr_fun(&reverse_range<std::string>)); 

EDIT:

Debido string :: string :: comenzar y terminar tener tanto const y no const variantes, es necesario para que los echasen (como litb descubrió mientras estaba fuera escribiendo a probar mi respuesta ... +1!). Esto lo hace muy detallado. Typedefs puede que sea un poco más higiénico, sino para seguir con el tema de una sola línea:

boost::bind(
    &std::reverse<std::string::iterator>, 
    boost::bind(
     (std::string::iterator (std::string::*)())&std::string::begin, _1), 
    boost::bind(
     (std::string::iterator (std::string::*)())&std::string::end, _1) 
    ) 
); 

¿Qué acaba de gritos de refactorización.

Por último, porque estoy aburrido, puntos de bonificación para C++ 0x:

std::for_each(v.begin(), v.end() [](std::string& s){ std::reverse(s); }); 

EDIT: boost :: bind funciona bien, sin necesidad de impulso :: lambda.

+0

¡Agradable! Eso es exactamente lo que quería saber. ¡Gracias! – fmuecke

+3

Lo intenté y este funciona: 'boost :: bind (& std :: reverse , boost :: bind ((std :: string :: iterator (std :: string :: *)()) & std :: string :: begin, _1), boost :: bind ((std :: string :: iterator (std :: string :: *)()) & std :: string :: end, _1)) ' –

+3

Seguramente, preferiría un bucle manual :) –

5

Usted tendría que rodar su propio objeto inversa:

struct Reverser 
{ 
    void operator()(std::string& value) const 
    { 
     std::reverse(value.begin(),value.end()); 
    } 
}; 

Ahora usted puede hacerlo una sola línea:

std::for_each(v.begin(), v.end(), Reverser()); 
+0

Como dije, no es un problema usar un objeto de función ... – fmuecke

5

Con Boost.Phoenix2:

std::for_each(v.begin(), v.end(), boost::phoenix::reverse(arg1)); 

Parafraseando mr-EDD: awesomer :)

ejemplo medible completo:

#include <boost/spirit/home/phoenix.hpp> 
#include <algorithm> 
#include <vector> 
#include <string> 
#include <iostream> 
#include <iterator> 

int main(void) 
{ 

    using namespace boost::phoenix::arg_names; // for "arg1" 

    std::vector<std::string> v; 
    v.push_back("hello"); 
    v.push_back("world"); 
    std::for_each(v.begin(), v.end(), boost::phoenix::reverse(arg1)); 

    std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(std::cout, "\n")); 
} 

impresiones:

olleh 
dlrow 
+0

+1 También me recuerda a mi propio Contenedor personal para todos los algoritmos STL, por lo que se pueden llamar en contenedores completos: range :: for_each (v, rango :: reverse ); - Simple :) – UncleBens

+0

+1 Agradable. Aunque adoro Boost.Spirit (la pura astucia de todo esto me da escalofríos, no importa que se compile (eventualmente) con algo tan pequeño y rápido), no le he dado mucho tiempo a Phoenix. Tal vez debería ir a afilar esa herramienta. –

+0

¡Ahh, phoenix! Lo amo. – fmuecke

3

podría b E también hacerse con la macro BOOST_FOREACH:

BOOST_FOREACH(std::string& s, v) 
    std::reverse(s.begin(), s.end()); 
+0

+1 ¡Eso también es muy bonito y compacto! – fmuecke

Cuestiones relacionadas