Как я могу перебрать два вектора одновременно, используя BOOST_FOREACH? - PullRequest
33 голосов
/ 02 сентября 2011

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

Ответы [ 4 ]

33 голосов
/ 02 сентября 2011

Итерация по двум вещам одновременно называется "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&), на самом деле нам нужен tuple<int&,int&>. Надеюсь, что это работает.

16 голосов
/ 27 ноября 2012

Если вы используете 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 :: Объединение не задокументировано.В любом случае, у меня работает.

8 голосов
/ 02 сентября 2011

Если вы хотите использовать 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 для C ++ 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 уже опубликовал решение Boost.

4 голосов
/ 13 июня 2012

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

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