Boost logo

Boost Users :

Subject: Re: [Boost-users] [range] Zip range adaptor
From: Nathan Crookston (nathan.crookston_at_[hidden])
Date: 2011-01-27 14:11:55


Hi Patrick,

On Thu, Jan 27, 2011 at 1:53 AM, Patrick Horgan <phorgan1_at_[hidden]> wrote:
>> On Sat, Jan 22, 2011 at 4:21 PM, Nathan Crookston
>>> I occasionally want to use something akin to MPL's zip_view and
>>> unpack_args with my ranges
> More details?

Fair enough -- I'll try to give some brief motivation and examples:

I often find that I have ranges whose elements are related to each
other. For example, I may have a list of images and a list of
offsets:

//Assume 0th elements of images and offsets are related, and so forth.
vector<Image> images(...);
vector<Offset> offsets(...);

If I wish to create a mosaic by adding each image/offset pair to an
aggregate image I currently have a few options:

//First option -- iterators for both. Requires an awful lot of
typing, introduces
// a new variable outside the for-loop scope. Code using an index is
much more concise,
// but both suffer from the problem of not being composable (more on
that later).
Mosaic mosaic;
vector<Offset>::iterator j = offsets.begin()
for(vector<Image>::iterator i = images.begin(); i != images.end() && j
!= offsets.end(); ++i, ++j)
  mosaic.add(*i, *j);

//Second option -- create a zip_iterator [1]. This obviously requires
a lot more typing.
// I think this is moving in the right direction.
for_each(boost::make_zip_iterator(boost::make_tuple(images.begin(),
offsets.begin())),
        boost::make_zip_iterator(boost::make_tuple(images.end(),
offsets.end())),
        AddToMosaic(mosaic));

I decided to create a new range adaptor based the zip_iterator which
reduces the verbosity of the loop declaration. Such an adaptor is
common for this problem [2][3]. I was surprised that I couldn't find
this already in the range library. (Feel free to disabuse me if
necessary.)

//Using the zip range adaptor. Creates begin and end zip_iterators
under the hood.
boost::for_each(zip(images, offsets), AddToMosaic(mosaic));

The previous looks much nicer and more expressive to my eye. Note
that we're not copying any elements -- it's a lazy adaptor, like the
other range adaptors. It's also composable:
//Passes elements 0,2,4, etc. packed into a tuple of references.
boost::for_each(zip(images, offsets) | strided(2), AddToMosaic(mosaic));

My implementation of zip supports multiple arguments (controlled by a
preprocessor symbol, currently 5). Hopefully my reasons for
suggesting zip are clear.

I also wrote a function corresponding to mpl::unpack_args. If we
examine the declaration of AddToMosaic it's clear that using zip() is
still burdening us with syntax contortions:

struct AddToMosaic
{
  AddToMosaic(Mosaic& rMosaic) : m_rMosaic(rMosaic) {}

  void operator()(const boost::tuple<Image&, Offset&>& tuple)
  { m_rMosaic.add(boost::get<0>(tuple), boost::get<1>(tuple)); }
};

It would be nice to have separate arguments rather than having to
write an adaptor functor for each use of zip():

boost::for_each(zip(images, offsets) | strided(2), unzip(AddToMosaic(mosaic)));

struct AddToMosaic
{
  AddToMosaic(Mosaic& rMosaic) : m_rMosaic(rMosaic) {}

  void operator()(const Image& image, const Offset& offset)
  { m_rMosaic.add(image, offset); }
};

The unpack() function would be useful for the zip_iterator as well.

For more examples of zip and unpack usage see my original post [4].
This has been very useful in my work, and I think it can be generally
useful.

Thanks,
Nate

[1] <http://www.boost.org/doc/libs/1_44_0/libs/iterator/doc/zip_iterator.html>
[2] <http://www.digitalmars.com/d/2.0/phobos/std_range.html#zip>
[3] <http://www.boost.org/doc/libs/1_44_0/libs/mpl/doc/refmanual/zip-view.html>
[4] <http://permalink.gmane.org/gmane.comp.lib.boost.user/65266>


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net