如何使用BOOST_FOREACH同时迭代两个向量?

时间:2023-01-14 14:05:25

I'd like to replicate the following with 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 );
}

4 个解决方案

#1


34  

Iterating over two things simultaneously is called a "zip" (from functional programming), and Boost has a zip iterator:

同时迭代两件事被称为“zip”(从函数式编程),Boost有一个zip迭代器:

The zip iterator provides the ability to parallel-iterate over several controlled sequences simultaneously. A zip iterator is constructed from a tuple of iterators. Moving the zip iterator moves all the iterators in parallel. Dereferencing the zip iterator returns a tuple that contains the results of dereferencing the individual iterators.

zip迭代器提供了并行迭代多个受控序列的能力。一个zip迭代器是由一个迭代器的tuple构建的。移动zip迭代器可以并行地移动所有的迭代器。取消该zip迭代器返回一个元组,该元组包含对单个迭代器取消引用的结果。

Note that it's an iterator, not a range, so to use BOOST_FOREACH you're going to have to stuff two of them into an iterator_range or pair. So it won't be pretty, but with a bit of care you can probably come up with a simple zip_range and write:

注意,它是一个迭代器,而不是一个范围,因此要使用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>());
}

Or special-case for 2 and use std::pair rather than boost::tuple.

或者用std::pair而不是boost::tuple。

I suppose that since doSomething might have parameters (int&, int&), actually we want a tuple<int&,int&>. Hope it works.

我想,既然doSomething可能有参数(int&int&intand),实际上我们想要一个tuple< intand,int&>。希望它的工作原理。

#2


16  

If you use boost, I think it should be as simple as:

如果你使用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

the strange part is that boost::combine is not documented. Works for me, anyway.

奇怪的部分是boost::合并没有文档化。工作对我来说,无论如何。

#3


8  

If you want to use BOOST_FOREACH to iterate two vectors simultenously, as you've done in your sample code, then you've to encapsulate both vectors in a wrapper class which should expose begin and end functions. These functions return custom iterator to be used to iterate over the wrapper which internally will iterate over the two vectors. Doesn't sound good, but that is what you've to do.

如果您想要使用BOOST_FOREACH来模拟两个向量,就像您在样例代码中所做的那样,那么您必须将两个向量封装在一个包装器类中,它应该公开开始和结束函数。这些函数返回自定义迭代器,用于遍历内部将遍历这两个向量的包装器。听起来不太好,但这就是你要做的。

This is my first attempt to implement this (minimal implementation just to demonstrate the basic idea):

这是我第一次尝试实现这个(最小实现只是为了演示基本思想):

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

And the following is the test code. Since it's using usual for loop, because ideone has not installed for boost for C++0x or I'm doing something wrong when including it.

下面是测试代码。因为它使用的是通常的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;
}

Output:

输出:

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

Demo : http://ideone.com/Hf667

演示:http://ideone.com/Hf667

This is good for experimentation and learning purpose only, as I don't claim it to be perfect. There can be lots of improvement. And @Steve already has posted boost's solution.

这只适合实验和学习目的,因为我并不认为它是完美的。可以有很多改进。@Steve已经发布了boost的解决方案。

#4


4  

Thanks to the answer of Steve Jessop and the great comments, I came up to the following solution, so if you find that nice, vote up Steve Jessop answer first. ;)

感谢Steve Jessop的回答和精彩的评论,我找到了下面的解决方案,所以如果你发现了这个问题,请先投票给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;
    }
}

#1


34  

Iterating over two things simultaneously is called a "zip" (from functional programming), and Boost has a zip iterator:

同时迭代两件事被称为“zip”(从函数式编程),Boost有一个zip迭代器:

The zip iterator provides the ability to parallel-iterate over several controlled sequences simultaneously. A zip iterator is constructed from a tuple of iterators. Moving the zip iterator moves all the iterators in parallel. Dereferencing the zip iterator returns a tuple that contains the results of dereferencing the individual iterators.

zip迭代器提供了并行迭代多个受控序列的能力。一个zip迭代器是由一个迭代器的tuple构建的。移动zip迭代器可以并行地移动所有的迭代器。取消该zip迭代器返回一个元组,该元组包含对单个迭代器取消引用的结果。

Note that it's an iterator, not a range, so to use BOOST_FOREACH you're going to have to stuff two of them into an iterator_range or pair. So it won't be pretty, but with a bit of care you can probably come up with a simple zip_range and write:

注意,它是一个迭代器,而不是一个范围,因此要使用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>());
}

Or special-case for 2 and use std::pair rather than boost::tuple.

或者用std::pair而不是boost::tuple。

I suppose that since doSomething might have parameters (int&, int&), actually we want a tuple<int&,int&>. Hope it works.

我想,既然doSomething可能有参数(int&int&intand),实际上我们想要一个tuple< intand,int&>。希望它的工作原理。

#2


16  

If you use boost, I think it should be as simple as:

如果你使用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

the strange part is that boost::combine is not documented. Works for me, anyway.

奇怪的部分是boost::合并没有文档化。工作对我来说,无论如何。

#3


8  

If you want to use BOOST_FOREACH to iterate two vectors simultenously, as you've done in your sample code, then you've to encapsulate both vectors in a wrapper class which should expose begin and end functions. These functions return custom iterator to be used to iterate over the wrapper which internally will iterate over the two vectors. Doesn't sound good, but that is what you've to do.

如果您想要使用BOOST_FOREACH来模拟两个向量,就像您在样例代码中所做的那样,那么您必须将两个向量封装在一个包装器类中,它应该公开开始和结束函数。这些函数返回自定义迭代器,用于遍历内部将遍历这两个向量的包装器。听起来不太好,但这就是你要做的。

This is my first attempt to implement this (minimal implementation just to demonstrate the basic idea):

这是我第一次尝试实现这个(最小实现只是为了演示基本思想):

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

And the following is the test code. Since it's using usual for loop, because ideone has not installed for boost for C++0x or I'm doing something wrong when including it.

下面是测试代码。因为它使用的是通常的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;
}

Output:

输出:

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

Demo : http://ideone.com/Hf667

演示:http://ideone.com/Hf667

This is good for experimentation and learning purpose only, as I don't claim it to be perfect. There can be lots of improvement. And @Steve already has posted boost's solution.

这只适合实验和学习目的,因为我并不认为它是完美的。可以有很多改进。@Steve已经发布了boost的解决方案。

#4


4  

Thanks to the answer of Steve Jessop and the great comments, I came up to the following solution, so if you find that nice, vote up Steve Jessop answer first. ;)

感谢Steve Jessop的回答和精彩的评论,我找到了下面的解决方案,所以如果你发现了这个问题,请先投票给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;
    }
}