Sequence-Zip Function For C++11

Sequence-zip function for C++11?

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.

Python's zip() equivalent in C or C++

int **zip(int *arr1, int *arr2, int length)
{
int **ret = new int*[length];
for(int i = 0; i<length; i++)
{
ret[i] = new int[2];
ret[i][0] = arr1[i];
ret[i][1] = arr2[i];
}
return ret;
}

Understanding zip function in Python and adapting it for C

Your code creates the eight vertices of an axis-aligned cube in a specific order. In C, a more or less faithful rendering of the Python code could look like this:

struct point {
double x, y, z;
};

double s = 1.0;
const double zz[2] = {-s / 2, s / 2};
const double xx[4] = {-s / 2, s / 2, s / 2, -s / 2}; // zipped list, left
const double yy[4] = {s / 2, s / 2, -s / 2, -s / 2}; // zipped list, right

struct point p[8];
unsigned n = 0;

for (unsigned i = 0; i < 2; i++) {
for (unsigned j = 0; j < 4; j++) {
p[n].x = xx[j];
p[n].y = yy[j];
p[n].z = zz[i];

n++;
}
}

C doesn't have generators, so his code just fills an array. Zip might look like a good way to achieve this in Python, but Python isn't C, so don't bother to write your own zip. A more typical rendering of your code in C might look like this:

struct point p[8] = {
{-s / 2, s / 2, -s / 2},
{ s / 2, s / 2, -s / 2},
{ s / 2, -s / 2, -s / 2},
{-s / 2, -s / 2, -s / 2},
{-s / 2, s / 2, s / 2},
{ s / 2, s / 2, s / 2},
{ s / 2, -s / 2, s / 2},
{-s / 2, -s / 2, s / 2}
};

Implementing meta-function zip in c++11

This is the shortest implementation I've discovered:

template <typename...> struct typelist { };   
template <typename A,typename B> struct prepend;
template <typename A,typename B> struct joincols;
template <typename...> struct zip;

template <typename A,typename... B>
struct prepend<A,typelist<B...> > {
typedef typelist<A,B...> type;
};

template <>
struct joincols<typelist<>,typelist<> > {
typedef typelist<> type;
};

template <typename A,typename... B>
struct joincols<typelist<A,B...>,typelist<> > {
typedef typename
prepend<
typelist<A>,
typename joincols<typelist<B...>,typelist<> >::type
>::type type;
};

template <typename A,typename... B,typename C,typename... D>
struct joincols<typelist<A,B...>,typelist<C,D...> > {
typedef typename
prepend<
typename prepend<A,C>::type,
typename joincols<typelist<B...>,typelist<D...> >::type
>::type type;
};

template <>
struct zip<> {
typedef typelist<> type;
};

template <typename A,typename... B>
struct zip<A,B...> {
typedef typename joincols<A,typename zip<B...>::type>::type type;
};

Printing compile-time integer sequence in C++11

Isn't clear to me what exactly do you want obtain and the meaning of what you have done (why Facility? why List inside facility?).

I just give you an example of how to write Print() without recursion, using an unused array (and defining IntList, as suggested by Yakk, taking inspiration from std::integer_sequence)

#include <iostream>
#include <functional>

template <typename T, T ... Nums>
struct IntList
{
static void Print (std::ostream & s = std::cout)
{
using unused = int[];
(void)unused { 0, ((void)(s << Nums << ", "), 0)... };
s << std::endl;
}
};

int main()
{
using List1 = IntList<int, 1, 2, 3>;
List1::Print();
}

If you can use C++17 instead of C++11/C++14, you can write Print() without the unused hack, simply unpacking Nums as follows

   static void Print (std::ostream & s = std::cout)
{ (s << ... << (s << Nums, ", ")) << std::endl; }

Regarding concat and sort, I suppose you want member function that return (by example, the concat) a IntList with a concatenation of the two lists of numbers.

The a simple concat example can be the following static member for IntList

template <T ... Nums2>
static constexpr IntList<T, Nums..., Nums2...>
Concat (IntList<T, Nums2...> const &)
{ return {}; }

So you can write something like

constexpr IntList<int, 1, 2, 3> l1;
constexpr IntList<int, 4, 5, 6> l2;

constexpr auto l3 = l1.Concat(l2);

l3.Print(); // print 1, 2, 3, 4, 5, 6,

I leave you the sort function as simple exercise :-)

boost zip_iterator and std::sort

You can't sort a pair of zip_iterators.

Firstly, make_zip_iterator takes a tuple of iterators as input, so you could call:

boost::make_zip_iterator(boost::make_tuple( ... ))

but that won't compile either, because keys and keys+N doesn't have the same type. We need to force keys to become a pointer:

std::sort(boost::make_zip_iterator(boost::make_tuple(+keys, +values)),
boost::make_zip_iterator(boost::make_tuple(keys+N, values+N)));

this will compile, but the sorted result is still wrong, because a zip_iterator only models a Readable iterator, but std::sort also needs the input to be Writable as described here, so you can't sort using zip_iterator.

How can I iterate over two vectors simultaneously using BOOST_FOREACH?

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

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.

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(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.

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



Related Topics



Leave a reply



Submit