2010-02-03 16 views
7

Utilizando el rango de bucles basado en C++ 0x, sé que seremos capaces de hacer:¿Cómo rebanar con el bucle for-range? C++ 0x

std::vector<int> numbers = generateNumbers(); 

for(int k : numbers) 
{ 
    processNumber(k); 
} 

(podría ser aún más fácil de escribir con lambda)

Pero, ¿cómo ¿Debo hacerlo si solo quiero aplicar processNumber (k) a una parte de los números? Por ejemplo, ¿cómo debería escribir esto para el bucle para aplicar processNumber() a la mitad (cabeza o cola) de los números? ¿Se permite "cortar" como en Python o Ruby?

+4

Sería probablemente más fácil de hacer 'std :: for_each (de, a , [] (int k) {processNumber (k);}); '. O debería proporcionar un por cada sub-rango compatible en ese vector. –

+0

Sí, soy consciente de eso. Solo quiero saber los límites del bucle for-range en C++ en comparación con otros lenguajes donde el corte es "fácil". – Klaim

+0

¿La solución 'for_each' que se muestra arriba no es" fácil "? – jalf

Respuesta

0

Algo como esto puede funcionar (sin marcar como no tengo acceso a un compilador de C++ 0x),

Editar: lo comprobó en VS10, por supuesto que tenía que corregir los errores numurous ....

Define una clase que es un proxy para cualquier contenedor y cuyo iterator s solo devuelve un subconjunto del contenedor. El ejemplo que proporciono es el más simple de la primera mitad, pero se puede hacer mucho más general.

template <class Container> 
class head_t { 
    Container& c_; 
public: 
    template <class T> 
    class iter { 
     T curr_; 
     const T& end_; 
     int limit_; // count how many items iterated 
    public: 
     iter(T curr, const T& end) 
      : curr_(curr) 
      , end_(end)    
      , limit_(std::distance(curr_, end_)/2) 
      { } 

     typename Container::value_type operator*() { return *curr_; } 

     // Do the equivilant for for operator++(int) 
     iter& operator++() { 
      if (--limit_ == 0) // finished our slice 
       curr_ = end_; 
      else 
       ++curr_; 
      return *this; 
     } 

     bool operator!=(const iter& i) const { 
      return curr_ != i.curr_; 
     } 
    }; 

    head_t(Container& c) : c_(c) {} 
    iter<typename Container::iterator> begin() { 
     return iter<typename Container::iterator>(c_.begin(), c_.end()); 
    } 

    iter<typename Container::iterator> end() { 
     return iter<typename Container::iterator>(c_.end(), c_.end()); 
    }  
}; 

template <class T> 
head_t<T> head(T& t) { return head_t<T>(t); } 

Y luego se utiliza en el bucle:

for(int k : head(numbers)) 
+0

Buena respuesta. Tenga en cuenta que los nombres que terminan en '_t' están reservados: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-ctidentifier –

+0

+1. Solo un par de sugerencias: debe derivar la clase iter de std :: iterator_traits .Pero desea que esto sea un iterador de entrada independientemente del contenedor, por lo que también debe agregar "typedef std :: input_iterator_tag iterator_category" al cuerpo de la clase. Además, olvidaste implementar operator ==. – Manuel

+0

Eso es mucho trabajo solo para lograr lo que podrías hacer con un "on_each" oneliner. – jalf

3

Una posibilidad podría ser de impulso iterator_range

(No tener un compilador que apoya basa alcance para, usando BOOST_FOREACH en lugar de eso. 'esperaríamos que el trabajo basado en el rango sea el mismo, siempre que el contenedor o rango tenga el método de inicio y finalización).

#include <boost/foreach.hpp> 
#include <boost/range/iterator_range.hpp> 
#include <iostream> 
#include <vector> 

int main() 
{ 
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
    BOOST_FOREACH(int n, boost::make_iterator_range(v.begin(), v.begin() + v.size()/2)) { 
     std::cout << n << '\n'; 
    } 
} 

Para mayor comodidad, también podría hacer su propia función de división, por lo que aceptaría índices en lugar de iteradores. Una vez más, podría basarse en boost.iterator_range, o no:

#include <cstddef> 
#include <iterator> 

template <class Iterator> 
class iter_pair 
{ 
public: 
    typedef Iterator iterator; 
    typedef Iterator const_iterator; //BOOST_FOREACH appears to want this 
    iter_pair(iterator first, iterator last): first(first), last(last) {} 
    iterator begin() const { return first; } 
    iterator end() const { return last; } 
private: 
    iterator first, last; 
}; 

template <class Container> 
struct iterator_type 
{ 
    typedef typename Container::iterator type; 
}; 

template <class Container> 
struct iterator_type<const Container> 
{ 
    typedef typename Container::const_iterator type; 
}; 

template <class Container> 
iter_pair<typename iterator_type<Container>::type> 
    slice(Container& c, size_t i_first, size_t i_last) 
{ 
    typedef typename iterator_type<Container>::type iterator; 
    iterator first = c.begin();   
    std::advance(first, i_first); 
    iterator last = first; 
    std::advance(last, i_last - i_first); 
    return iter_pair<iterator>(first, last); 
} 

template <class Container> 
iter_pair<typename iterator_type<Container>::type> 
    slice(Container& c, size_t i_last) 
{ 
    return slice(c, 0, i_last); 
} 

//could probably also be overloaded for arrays 

#include <cctype> 
#include <string> 
#include <boost/foreach.hpp> 
#include <iostream> 

int main() 
{ 
    std::string s("Hello world, la-la-la!"); 
    BOOST_FOREACH(char& c, slice(s, 2, 11)) { 
     if (c == 'l') 
      c = std::toupper(c); 
    } 
    const std::string& r = s; 
    BOOST_FOREACH(char c, slice(r, r.size() - 1)) { 
     std::cout << c << " "; 
    } 
    std::cout << '\n'; 
} 

Generalmente uno probablemente estaría trabajando con iteradores en primer lugar, por lo que podría no ser tan útil.

+0

+1 Buena solución, definitivamente es el camino a seguir si está acostumbrado a trabajar con rangos (en lugar de trabajar con iteradores). Una optimización que sugeriría es colocar la línea "iterator last = first"; debajo de la línea que avanza "primero". Por supuesto, esto significa que "último" debe ser avanzado por "i_last-i_first", no "i_last". – Manuel

+0

Sí, es genial, pero lo que estoy buscando es una forma de hacerlo. Siento que el bucle for-range podría estar "incompleto" sin cortar, pero quizás no sé cómo hacer slicing con esta nueva sintaxis. – Klaim

+1

@Klaim: ¿No funciona con el rango de? No puedo probar y no tengo conocimiento de ninguna nueva sintaxis de corte especial. Por lo que yo sé, todo lo que proporciona un método begin() y end() que devuelve cosas similares a iteradores va. @ Manuel: Gracias, código editado. – UncleBens

11

Puede utilizar el "sliced" range adaptor de la biblioteca Boost.Range:

#include <boost/range/adaptor/sliced.hpp> 

using boost::adaptors::sliced; 

...

std::vector<int> numbers = generateNumbers(); 
for(int k : numbers | sliced(0, numbers.size()/2)) 
{ 
    processNumber(k); 
}