Подтвердить что ты не робот

Как я могу перебирать два вектора одновременно с помощью BOOST_FOREACH?

Я хотел бы повторить следующее с 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 );
}
4b9b3361

Ответ 1

Итерация по двум вещам одновременно называется "zip" (от функционального программирования), а Boost имеет zip-итератор:

Ипотека zip обеспечивает возможность параллельного итерации по нескольким контролируемых последовательностей. Создан zip-итератор из кортежа итераторов. Перемещение итератора zip перемещает все итераторы параллельно. Развертывание zip-итератора возвращает кортеж который содержит результаты разыменования отдельных итераторов.

Обратите внимание, что это итератор, а не диапазон, поэтому для использования BOOST_FOREACH вам придется записать два из них в iterator_range или pair. Так что это будет некрасиво, но с некоторой осторожностью вы, вероятно, можете придумать простой zip_range и написать:

BOOST_FOREACH(boost::tuple<int,int> &p, zip_range(v1, v2)) {
    doSomething(p.get<0>(), p.get<1>());
}

Или специальный случай для 2 и используйте std::pair, а не boost::tuple.

Я полагаю, что поскольку doSomething может иметь параметры (int&, int&), на самом деле мы хотим a tuple<int&,int&>. Надеюсь, что это сработает.

Ответ 2

Если вы используете boost, я думаю, что он должен быть таким простым, как:

#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

странная часть заключается в том, что boost:: combination не документируется. Во всяком случае, работает для меня.

Ответ 3

Если вы хотите использовать BOOST_FOREACH для одновременного итерации двух векторов, как это было в вашем примере кода, вы должны инкапсулировать оба вектора в класс-оболочку, который должен выставлять функции begin и end, Эти функции возвращают пользовательский итератор, который будет использоваться для итерации по оболочке, которая внутренне будет перебирать эти два вектора. Звучит неплохо, но это то, что вы должны делать.

Это моя первая попытка реализовать эту (минимальную реализацию, чтобы продемонстрировать основную идею):

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_;
    }    
};

И следующий тестовый код. Поскольку он использует обычный цикл for, потому что ideone не установлен для boost для С++ 0x, или я делаю что-то неправильно, когда включаю его.

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;
}

Вывод:

1, 11
2, 12
3, 13
4, 14
5, 15

Демо: http://ideone.com/Hf667

Это полезно только для экспериментирования и обучения, так как я не утверждаю, что это идеально. Там может быть много улучшений. И @Steve уже опубликовал решение для повышения производительности.

Ответ 4

Благодаря ответу Стива Джессопа и замечательным комментариям, я подошел к следующему решению, поэтому, если вы найдете это хорошо, сначала попробуйте ответить Стив Джессоп.;)

#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;
    }
}