Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ range/xrange equivalent in STL or boost?

Is there C++ equivalent for python Xrange generator in either STL or boost?

xrange basically generates incremented number with each call to ++ operator. the constructor is like this:

xrange(first, last, increment)

was hoping to do something like this using boost for each:

foreach(int i, xrange(N))

I. am aware of the for loop. in my opinion they are too much boilerplate.

Thanks

my reasons:

my main reason for wanting to do so is because i use speech to text software, and programming loop usual way is difficult, even if using code completion. It is much more efficient to have pronounceable constructs.

many loops start with zero and increment by one, which is default for range. I find python construct more intuitive

 for(int i = 0; i < N; ++i)
 foreach(int i, range(N))

functions which need to take range as argument:

 Function(int start, int and, int inc);
 function(xrange r);

I understand differences between languages, however if a particular construct in python is very useful for me and can be implemented efficiently in C++, I do not see a reason not to use it. For each construct is foreign to C++ as well however people use it.

I put my implementation at the bottom of the page as well the example usage.

in my domain i work with multidimensional arrays, often rank 4 tensor. so I would often end up with 4 nested loops with different ranges/increments to compute normalization, indexes, etc. those are not necessarily performance loops, and I am more concerned with correctness readability and ability to modify.

for example

int function(int ifirst, int ilast, int jfirst, int jlast, ...);
versus
int function(range irange, range jrange, ...);

In the above, if different strids are needed, you have to pass more variables, modify loops, etc. eventually you end up with a mass of integers/nearly identical loops.

foreach and range solve my problem exactly. familiarity to average C++ programmer is not high on my list of concerns - problem domain is a rather obscure, there is a lot of meta-programming, SSE intrinsic, generated code.

like image 812
Anycorn Avatar asked Dec 29 '09 22:12

Anycorn


2 Answers

Boost irange should really be the answer (ThxPaul Brannan)

I'm adding my answer to provide a compelling example of very valid use-cases that are not served well by manual looping:

#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/irange.hpp>

using namespace boost::adaptors;

static int mod7(int v) 
    { return v % 7; }

int main() 
{
    std::vector<int> v;

    boost::copy(
            boost::irange(1,100) | transformed(mod7), 
            std::back_inserter(v));

    boost::sort(v);

    boost::copy(
            v | reversed | uniqued, 
            std::ostream_iterator<int>(std::cout, ", "));
}

Output: 6, 5, 4, 3, 2, 1, 0,

Note how this resembles generators/comprehensions (functional languages) and enumerables (C#)

Update I just thought I'd mention the following (highly inflexible) idiom that C++11 allows:

for (int x : {1,2,3,4,5,6,7})
    std::cout << x << std::endl;

of course you could marry it with irange:

for (int x : boost::irange(1,8))
    std::cout << x << std::endl;
like image 53
sehe Avatar answered Oct 11 '22 07:10

sehe


Boost has counting_iterator as far as I know, which seems to allow only incrementing in steps of 1. For full xrange functionality you might need to implement a similar iterator yourself.

All in all it could look like this (edit: added an iterator for the third overload of xrange, to play around with boost's iterator facade):

#include <iostream>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/foreach.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <cassert>

template <class T>
boost::iterator_range<boost::counting_iterator<T> > xrange(T to)
{
    //these assertions are somewhat problematic:
    //might produce warnings, if T is unsigned
    assert(T() <= to);
    return boost::make_iterator_range(boost::counting_iterator<T>(0), boost::counting_iterator<T>(to));
}

template <class T>
boost::iterator_range<boost::counting_iterator<T> > xrange(T from, T to)
{
    assert(from <= to);
    return boost::make_iterator_range(boost::counting_iterator<T>(from), boost::counting_iterator<T>(to));
}

//iterator that can do increments in steps (positive and negative)
template <class T>
class xrange_iterator:
    public boost::iterator_facade<xrange_iterator<T>, const T, std::forward_iterator_tag>
{
    T value, incr;
public:
    xrange_iterator(T value, T incr = T()): value(value), incr(incr) {}
private:
    friend class boost::iterator_core_access;
    void increment() { value += incr; }
    bool equal(const xrange_iterator& other) const
    {
        //this is probably somewhat problematic, assuming that the "end iterator"
        //is always the right-hand value?
        return (incr >= 0 && value >= other.value) || (incr < 0 && value <= other.value);
    }
    const T& dereference() const { return value; }
};

template <class T>
boost::iterator_range<xrange_iterator<T> > xrange(T from, T to, T increment)
{
    assert((increment >= T() && from <= to) || (increment < T() && from >= to));
    return boost::make_iterator_range(xrange_iterator<T>(from, increment), xrange_iterator<T>(to));
}

int main()
{
    BOOST_FOREACH(int i, xrange(10)) {
        std::cout << i << ' ';
    }
    BOOST_FOREACH(int i, xrange(10, 20)) {
        std::cout << i << ' ';
    }
    std::cout << '\n';
    BOOST_FOREACH(int i, xrange(0, 46, 5)) {
        std::cout << i << ' ';
    }
    BOOST_FOREACH(int i, xrange(10, 0, -1)) {
        std::cout << i << ' ';
    }
}

As others are saying, I don't see this buying you much over a normal for loop.

like image 36
UncleBens Avatar answered Oct 11 '22 06:10

UncleBens