Ivor Denham-Dyson
Ivor Denham-Dyson

Reputation: 685

A post-increment operator that allows one to increment at a specified step

Which is worse?

Creating copies

#include <vector>
#include <algorithm>
template<class T>
std::vector<T> range(const T start, const T stop, const T step) {
    int leaps = ((stop-start)/step);
    std::vector<T> output(leaps > 0 ? leaps : -leaps);
    std::generate(output.begin(), output.end(), [i = start, step] () mutable {
        T num = i;
        i+=step;
        return num;
    });
    return output;
}

or repeating (I'm assuming a single calculation).

#include <vector>
#include <algorithm>
template<class T>
std::vector<T> range(const T start, const T stop, const T step) {
    int leaps = ((stop-start)/step);
    std::vector<T> output(leaps > 0 ? leaps : -leaps);
    std::generate(output.begin(), output.end(), [i = start-step, step] () mutable {return i+=step;});
    return output;
}

Is there a way to avoid both? Something such as a post-increment operator that behaves as i++ but allows an increment of step.

// Example
int main() {
    std::vector<double> check_range = range(-4.13, 2.13, 0.25);
    return 0;
}

Expected

-4.13, -3.88, -3.63, -3.38, -3.13, -2.88, -2.63, -2.38, -2.13, -1.88, -1.63, -1.38, -1.13, -0.88, -0.63, -0.38, -0.13, 0.12, 0.37, 0.62, 0.87, 1.12, 1.37, 1.62, 1.87

Upvotes: 0

Views: 125

Answers (2)

Barry
Barry

Reputation: 303206

In C++20, I'd write it lazily:

template <class T>
auto range(const T start, const T stop, const T step) {
    return views::iota(0)
         | views::transform([=](int i) -> T{
               return i * step + start;
           })
         | views::take_while([=](T cur){
               return cur < stop;
           });
}

If you really want a vector, you can eagerly evaluate that, but you probably don't need all of it at once?


You could also write a generator using coroutines (though generator<T> isn't in the standard library, need to use like cppcoro):

template <class T>
generator<T> range(T start, const T stop, const T step) {
    for (; start < stop; start += stop) {
        co_yield start;
    }
}

Which, likewise, is a lazy range that can be eagerly evaluated into a vector if you really want.

Upvotes: 2

Caleth
Caleth

Reputation: 62884

Have a look at boost::irange. If that doesn't do what you need, you can do some arithmetic.

template<class T>
auto range(const T start, const T stop, const T step) {
    int leaps = ((stop-start)/step);
    auto toT = [start, step](int i) { return start + (i * step); };
    return boost::irange(0, leaps > 0 ? leaps : -leaps) | boost::adaptors::transformed(toT);
}

Upvotes: 0

Related Questions