c++ - ¿Cómo puedo iterar sobre dos vectores simultáneamente usando BOOST_FOREACH?
iterator boost-foreach (4)
Al iterar más de dos cosas simultáneamente se le llama "zip" (de la programación funcional), y Boost tiene un iterador zip :
El iterador zip proporciona la capacidad de iterar en paralelo sobre varias secuencias controladas simultáneamente. Un iterador zip se construye a partir de una tupla de iteradores. Mover el iterador zip mueve todos los iteradores en paralelo. La desreferenciación del iterador zip devuelve una tupla que contiene los resultados de la desreferenciación de los iteradores individuales.
Tenga en cuenta que es un iterador, no un rango, por lo que para usar BOOST_FOREACH
tendrá que rellenar dos de ellos en un iterator_range o un pair
. Por lo tanto, no será bonito, pero con un poco de cuidado, probablemente puedas crear un simple zip_range
y escribir:
BOOST_FOREACH(boost::tuple<int,int> &p, zip_range(v1, v2)) {
doSomething(p.get<0>(), p.get<1>());
}
O caso especial para 2 y use std::pair
lugar de boost::tuple
.
Supongo que dado que doSomething
puede tener parámetros (int&, int&)
, en realidad queremos una tuple<int&,int&>
. Espero que funcione.
Me gustaría replicar lo siguiente con BOOST FOREACH
std::vector<int>::const_iterator i1;
std::vector<int>::const_iterator i2;
for( i1 = v1.begin(), i2 = v2.begin();
i1 < v1.end() && i2 < v2.end();
++i1, ++i2 )
{
doSomething( *i1, *i2 );
}
Gracias a la respuesta de Steve Jessop y a los excelentes comentarios, se me ocurrió la siguiente solución, así que si le parece agradable, vote primero por Steve Jessop. ;)
#include <iostream>
#include <vector>
#include <boost/typeof/typeof.hpp>
#include <boost/typeof/std/vector.hpp>
#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range/iterator_range.hpp>
using namespace boost;
int main(int argc, char **argv) {
std::vector<int> vecFirst = assign::list_of(1)(2)(3)(43)(7)(13);
std::vector<double> vecSecond = assign::list_of(53.45)(-23.545)(0.1574)(1.001)(0.0047)(9.7);
BOOST_AUTO(zipSequence,
make_iterator_range(
make_zip_iterator(make_tuple(vecFirst.begin(), vecSecond.begin())),
make_zip_iterator(make_tuple(vecFirst.end(), vecSecond.end()))
)
);
BOOST_FOREACH( BOOST_TYPEOF(*zipSequence.begin()) each, zipSequence) {
std::cout << "First vector value : " << each.get<0>()
<< " - Second vector value : " << each.get<1>()
<< std::endl;
}
}
Si desea usar BOOST_FOREACH
para iterar dos vectores simultáneamente, como lo ha hecho en su código de muestra, entonces debe encapsular ambos vectores en una clase contenedora que debe exponer las funciones de begin
y end
. Estas funciones devuelven un iterador personalizado que se utilizará para iterar sobre el contenedor que internamente iterará sobre los dos vectores. No suena bien, pero eso es lo que tienes que hacer.
Este es mi primer intento de implementar esto ( implementación mínima solo para demostrar la idea básica ):
template<typename T>
struct wrapper
{
struct iterator
{
typedef typename std::vector<T>::iterator It;
It it1, it2;
iterator(It it1, It it2) : it1(it1), it2(it2) {}
iterator & operator++()
{
++it1; ++it2; return *this;
}
iterator & operator *()
{
return *this;
}
bool operator == (const iterator &other)
{
return !(*this != other);
}
bool operator != (const iterator &other)
{
return it1 != other.it1 && it2 != other.it2;
}
};
iterator begin_, end_;
wrapper(std::vector<T> &v1, std::vector<T> &v2)
: begin_(v1.begin(), v2.begin()),end_(v1.end(), v2.end())
{
}
wrapper(const wrapper & other) : begin_(other.begin_), end_(other.end_) {}
iterator begin()
{
return begin_;
}
iterator end()
{
return end_;
}
};
Y el siguiente es el código de prueba. Dado que está utilizando el bucle usual for
, porque ideone no se ha instalado para boost para C ++ 0x o estoy haciendo algo mal al incluirlo.
int main() {
std::vector<int> v1 = {1,2,3,4,5,6};
std::vector<int> v2 = {11,12,13,14,15};
wrapper<int> w(v1,v2);
for(wrapper<int>::iterator it = w.begin(); it != w.end(); ++it)
{
std::cout << *it.it1 <<", "<< *it.it2 << std::endl;
}
return 0;
}
Salida:
1, 11
2, 12
3, 13
4, 14
5, 15
Demostración: http://ideone.com/Hf667
Esto es bueno solo para fines de experimentación y aprendizaje, ya que no pretendo que sea perfecto. Puede haber muchas mejoras. Y @Steve ya ha publicado la solución de boost.
Si usas boost, creo que debería ser tan simple como:
#include <boost/foreach.hpp>
#include <boost/range/combine.hpp>
std::vector<int> v1;
std::vector<int> v2;
// iterate over values
int i1, i2;
BOOST_FOREACH(boost::tie(i1, i2), boost::combine(v1, v2))
std::cout << i1+i2 << "/n"; // sums two vectors
// iterate over references
typedef boost::tuple<int&, int&> int_ref_tuple;
BOOST_FOREACH(int_ref_tuple tup, boost::combine(v1, v2))
tup.get<0>() = tup.get<1>(); // assigns one vector to another
la parte extraña es que boost :: combine no está documentado. Funciona para mí, de todos modos.