views:

541

answers:

9

hello

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.

+4  A: 

You're trying to bring a python idiom into C++. That's unncessary. Use

for(int i=initVal;i<range;i+=increment) 
{ 
    /*loop body*/
}

to achieve this. In Python, the for(i in xrange(init, rng, increment)) form is necessary because Python doesn't provide a simple for loop, only a for-each type construct. So you can iterate only over a sequence or a generator. This is simply unnecessary and almost certainly bad practice in a language that provides a for(;;) syntax.

EDIT: As a completely non-recommended aside, the closest I can get to the for i xrange(first, last, inc) syntax in C++ is:

#include <cstdio>

using namespace std;

int xrange(unsigned int last, unsigned int first=0, unsigned int inc=1)
{
    static int i = first;
    return (i<last)?i+=inc:i=0;
}

int main()
{
    while(int i=xrange(10, 0, 1))
        printf("in loop at i=%d\n",i);
}

Not that while this loops the correct number of times, i varies from first+inc to last and NOT first to last-inc as in Python. Also, the function can only work reliably with unsigned values, as when i==0, the while loop will exit. Do not use this function. I only added this code here to demonstrate that something of the sort is indeed possible. There are also several other caveats and gotchas (the code won't really work for first!=0 on subsequent function calls, for example)

Chinmay Kanchi
+1  A: 

The for loop handles that nearly automatically:

for(int loop=first;loop < last;loop += increment)
{
  /// Do Stuff.
}
Martin York
+1  A: 

What you're doing isn't go to work as efficiently as you intend. BOOST_FOREACH evaluates it's arguments only once. This means you need xrange to produce an iteratable container full of your values.

That is, it could look like this (note, could be more generic, like taking an advancement functor, etc.):

template <typename T>
std::vector<T> xrange(const T& pBegin, const T& pEnd)
{
    std::vector<T> v;

    for (T i = pBegin, i < pEnd; ++i)
    {
        v.push_back(i);
    }

    return v;
}

But all you've done is:

  1. Move the for-loop into a function,
  2. Wasted time and resources allocating a vector that will die shortly after

Rather, just use the for-loop directly, like I previously mentioned and other are mentioning.

If you're really against a loop (keep in mind you don't program to save keystrokes!), you could probably use a macro and some magic (untested):

#define FOR_N(v, s, e)  for (BOOST_AUTO(v, s); v < e; ++v)

#define for_n FOR_N // better looking

for_n(i, 0, N)
{
    // use i;
}

Of course now you have to include another file, and this code is :\

GMan
I have have considered this approach, but it does not work for me.i was thinking along the lines of function object with ++ operator.i have multiple nested loops, and regular loops make code very busy.
aaa
If you post the real code, we'll help you make it look nice. But this is C++, not Python, and a simple-for loop is **far** more efficient than what I've posted.
GMan
i put implementation and example case at the bottom of discussion, last answer.
aaa
+8  A: 

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.

UncleBens
+2  A: 
aaa
how is foreach implemented? And I assume that the `??`'s were meant to be `<<`, right?
jalf
A: 

std::iota (not yet standardized) is kinda like range. Doesn't make things any shorter or clearer than an explicit for loop, though.

#include <algorithm>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
int main() {
    std::vector<int> nums(5);
    std::iota(nums.begin(), nums.end(), 1);
    std::copy(nums.begin(), nums.end(),
            std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
    return 0;
}

Compile with g++ -std=c++0x; this prints "1 2 3 4 5 \n".

ephemient
+1  A: 

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.

That makes sense. But couldn't a simple macro solve this problem? #define for_i_to(N, body) for (int i = 0; i < N; ++i) { body }

or something similar. Or avoid the loop entirely and use the standard library algorithms. (std::for_each(range.begin(), rang.end(), myfunctor()) seems easier to pronounce)

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

You're wrong. The Python version is more intuitive to a Python programmer. And it may be more intuitive to a non-programmer. But you're writing C++ code. Your goal should be to make it intuitive to a C++ programmer. And C++ programmer know for-loops and they know the standard library algorithms. Stick to using those. (Or stick to writing Python)

functions which need to take range as argument:

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

Or the idiomatic C++ version:

template <typename iter_type>
void function(iter_type first, iter_type last);

In C++, ranges are represented by iterator pairs. Not integers. If you're going to write code in a new language, respect the conventions of that language. Even if it means you have to adapt and change some habits.

If you're not willing to do that, stick with the language you know.

Trying to turn language X into language Y is always the wrong thing to do. It own't work, and it'll confuse the language X programmers who are going to maintain (or just read) your code.

jalf
macros have problems of their own and i dislike using them for a number of reasons except for a few special cases.for_each requires too much extra code for my purposes.By your reasoning, one should not use much outside of the standard library, even if it makes his/her life easier.should they not use boost either? should they not use template expressions? I would bet majority of C++ programmers would have hard time parsing te programs, but it is hardly a reason not to use them.
aaa
No, by my reasoning, one should stick to the same *style* as used by the standard lbirray. (or Boost). I'm not saying "don't add anything to the language". I'm saying "Don't add something that runs contrary to what a C++ programmer expects". A C++ programmer expects a range to be represented by an iterator pair, not a homerolled "range" object. Macros have plenty of problems on their own, yes, but they might solve your specific problem: "I need a for loop, but they're hard to pronounce". That is all your question boils down to.
jalf
foreach loop, Matlab matrix notation, Fortran memory ordering are not the "expected" style either. however they, as well as range object, work very well for my specific domain. I asked a technical question, instead you try to insult me with a long rant. I do not care about your opinions.
aaa
Insult? No, I answered your question as best I could. I don't see what matlab or fortran memory ordering has to do with how people expect a for loop in C++ to look. If you feel insulted by the message that "either you should use the language as it was intended to be used, or you should use a language that works the way you *want*", then you really need to grow some thicker skin.
jalf
please show me where C++ was intended to be meta-programming language/DSEL environment.
aaa
At the point where people found out that it was 1) possible, and 2) useful. At the point where C++ experts started recommending it. At the point where C++ programmers got used to the idea.At the point where meta-programming introduced *new* capabilities into the language. Importing Python's workaround for its lack of a regular for loop into a language that *does* have a for loop is none of those things. It is just pointless.
jalf
But I'm not sure why you're so upset. All I'm saying is that "C++ has a for loop which does **exactly** what you need. It also has iterators, which are an idiomatic way to represent ranges.Since you're coming from Python, you're probably familiar with the Zen of Python. Remember the line that goes "There should be one-- and preferably only one --obvious way to do it"? So why exactly should we add *another* syntax for for loops, and *another* representation for ranges? Especially when it would break interoperability with existing code (which uses iterator pairs for ranges)?
jalf
i dislike people pushing their opinion without reading the problem. please read my implementation and the sample test case I have posted before you wrote your message. then tell me if you honestly think iterator pair would be more suitable for multiple nested loops with different bounds/increments. FYI, I have picked up python after C/Java/Fortran. different languages have particular constructs in certain domains which should not be overlooked just because it is not the way you do it.
aaa
I dislike people omitting relevant information from the question and then throwing a goddamn tantrum when people don't read their minds. Where does your question say anything about nested loops with different bounds/increments? Why isn't your implementation and test case IN THE QUESTION WE ARE SUPPOESED TO ANSWER? Anyway, if you've already tailored your code base to use your hypothetical range objects then yes, your range objects will magically be a perfect fit. Surprising, isn't it?
jalf
Since we don't know what you're using the ranges for, it's impossible for any of us to contribute anything useful. "Oh yes, you could implement the range object you already wrote your code to expect". Gee, that's helpful, and tells you a lot you didn't already know. Why do you need the actual indices for your ranges? How many nested loops are you going to have? You've posed a question that is impossible to answer. Half the information we need is missing, and all your code is tailored to the answer you *want* to hear. And you get hysterical when people answer differnetly than you wanted.
jalf
A: 

Since we don't really know what you actually want to use this for, I'm assuming your test case is representative. And then plain simple for loops are a whole lot simpler and more readable:

int main() {
  for (int i = 0; i <= 6; ++i){
    for (int j = 3; j <= 10; j += 3){
      std::cout << i << " " << j << "\n";
    }
  }
}

A C++ programmer can walk in from the street and understand this function without having to look up complex classes elsewhere. And it's 5 lines instead of your 60. Of course if you have 400 loops exactly like these, then yes, you'd save some effort by using your range object. Or you could just wrap these two loops inside a helper function, and call that whenever you needed.

We don't really have enough information to say what's wrong with simple for loops, or what would be a suitable replacement. The loops here solve your problem with far less complexity and far fewer lines of code than your sample implementation. If this is a bad solution, tell us your requirements (as in what problem you need to solve, rather than "I want python-style loops in C++")

jalf
please, go away.uncleBens posted code, I accepted his answer. I would also appreciate if you remove your posts, they are not helpful.
aaa
Perhaps you could point me to the place in the FAQ where it says that you own the site? Or that questions should be posed as unhelpfully as possible and that answers should be discouraged? Or that other answer should be *deleted* once one has been accepted?Btw, I love that you accepted an answer which says "As others are saying, I don't see this buying you much over a normal for loop". But I'm sure you chose to ignore that part of the answer.Hope you have a great new year. It sounds like you could do with some time away from the computer.
jalf
It may surprise you, but one of the goals of SO is that questions should be *generally* useful. That means that answers other than the accepted one should stay around because *others* who have a similar question might find them useful. The question isn't *yours and yours alone*. You asked it, but many people may benefit from the answers. I would appreciate it if you stopped acting like such a drama queen and just did what SO is intended for: Asked useful questions and repped/accepted useful answers. Without all the drama and hysterics
jalf
if you think people will benefit from your comments, they should of course remain. I myself do not see much merit in them.
aaa
A: 

Since I've started to use BOOST_FOREACH for all my iteration (probably a misguided idea, but that's another story), here's another use for aaa's range class:

std::vector<int> vec;
// ... fill the vector ...
BOOST_FOREACH(size_t idx, make_range(0, vec.size()))
{
  // ... do some stuff ...
}

(yes, range should be templatized so I can use user-defined integral types with it)

And here's make_range():

template<typename T>
range<T> make_range(T const & start, T const & end)
{
  return range<T>(start, end);
}

See also:

http://groups.google.com/group/boost-list/browse_thread/thread/3e11117be9639bd

and:

https://svn.boost.org/trac/boost/ticket/3469

which propose similar solutions.

And I've just found boost::integer_range; with the above example, the code would look like:

using namespace boost;
std::vector<int> vec;
// ... fill the vector ...
BOOST_FOREACH(size_t idx, make_integer_range(0, vec.size()))
{
  // ... do some stuff ...
}
Paul Brannan