web-dev-qa-db-ja.com

BOOST_FOREACHを使用して2つのベクトルを同時に反復するにはどうすればよいですか?

以下を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 );
}
33
Candy Chiu

2つのものを同時に反復することは(関数プログラミングから)「Zip」と呼ばれ、 BoostにはZipイテレータがあります

Zipイテレーターは、複数の制御シーケンスを同時に並列反復する機能を提供します。 Zipイテレータは、イテレータのタプルから構築されます。 Zipイテレーターを移動すると、すべてのイテレーターが並行して移動します。 Zipイテレーターを逆参照すると、個々のイテレーターを逆参照した結果を含むタプルが返されます。

これは範囲ではなくイテレータなので、BOOST_FOREACHを使用するには、2つを 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&>が必要だと思います。それがうまくいくことを願っています。

33
Steve Jessop

あなたがブーストを使うなら、私はそれが次のように単純であるべきだと思います:

#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 :: combineが文書化されていないことです。とにかく私のために働く。

16
panda-34

サンプルコードで行ったように、BOOST_FOREACHを使用して2つのベクトルを同時に反復処理する場合は、begin関数とend関数を公開するラッパークラスに両方のベクトルをカプセル化する必要があります。これらの関数は、内部で2つのベクトルを反復するラッパーを反復するために使用されるカスタムイテレータを返します。よく聞こえませんが、それはあなたがしなければならないことです。

これはこれを実装する私の最初の試みです(基本的なアイデアを示すためだけの最小限の実装):

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は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のソリューションを投稿しています。

8
Nawaz

スティーブジェソップの回答と素晴らしいコメントのおかげで、私は次の解決策を思いついたので、ニースを見つけたら、スティーブジェソップの回答を最初に投票してください。 ;)

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