Sequence-zip function for c++11?

Hooked picture Hooked · Dec 14, 2011 · Viewed 59.9k times · Source

With the new range-based for loop we can write code like

for(auto x: Y) {}

Which IMO is a huge improvement from (for ex.)

for(std::vector<int>::iterator x=Y.begin(); x!=Y.end(); ++x) {}

Can it be used to loop over two simultaneous loops, like Pythons zip function? For those unfamiliar with Python, the code:

Y1 = [1,2,3]
Y2 = [4,5,6,7]
for x1,x2 in zip(Y1,Y2):
    print x1,x2

Gives as output (1,4) (2,5) (3,6)

Answer

kennytm picture kennytm · Dec 15, 2011

Warning: boost::zip_iterator and boost::combine as of Boost 1.63.0 (2016 Dec 26) will cause undefined behavior if the length of the input containers are not the same (it may crash or iterate beyond the end).


Starting from Boost 1.56.0 (2014 Aug 7) you could use boost::combine (the function exists in earlier versions but undocumented):

#include <boost/range/combine.hpp>
#include <vector>
#include <list>
#include <string>

int main() {
    std::vector<int> a {4, 5, 6};
    double b[] = {7, 8, 9};
    std::list<std::string> c {"a", "b", "c"};
    for (auto tup : boost::combine(a, b, c, a)) {    // <---
        int x, w;
        double y;
        std::string z;
        boost::tie(x, y, z, w) = tup;
        printf("%d %g %s %d\n", x, y, z.c_str(), w);
    }
}

This would print

4 7 a 4
5 8 b 5
6 9 c 6

In earlier versions, you could define a range yourself like this:

#include <boost/iterator/zip_iterator.hpp>
#include <boost/range.hpp>

template <typename... T>
auto zip(T&&... containers) -> boost::iterator_range<boost::zip_iterator<decltype(boost::make_tuple(std::begin(containers)...))>>
{
    auto zip_begin = boost::make_zip_iterator(boost::make_tuple(std::begin(containers)...));
    auto zip_end = boost::make_zip_iterator(boost::make_tuple(std::end(containers)...));
    return boost::make_iterator_range(zip_begin, zip_end);
}

The usage is the same.