Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining multiple for loops into single iterator

Tags:

c++

loops

c++14

Say I have a nest for loop like

for (int x = xstart; x < xend; x++){
    for (int y = ystart; y < yend; y++){
        for (int z = zstart; z < zend; z++){
            function_doing_stuff(std::make_tuple(x, y, z));
        }
    }
}

and would like to transform it into

MyRange range(xstart,xend,ystart,yend, zstart,zend);
for (auto point : range){
    function_doing_stuff(point);
}

How would I write the MyRange class to be as efficient as the nested for loops? The motivation for this is to be able to use std algorithms (such as transform, accumulate, etc), and to create code that is largely dimension agnostic.

By having an iterator, it would be easy to create templated functions that operate over a range of 1d, 2d or 3d points.

Code base is currently C++14.

EDIT:

Writing clear questions is hard. I'll try to clarify. My problem is not writing an iterator, that I can do. Instead, the problem is one of performance: Is it possible to make an iterator that is as fast as the nested for loops?

like image 602
LKlevin Avatar asked Oct 15 '18 11:10

LKlevin


People also ask

Can you combine for loops?

A reproducible example would help, but the short answer is yes.

Can you combine 2 for loops python?

Nested For LoopsLoops can be nested in Python, as they can with other programming languages. The program first encounters the outer loop, executing its first iteration. This first iteration triggers the inner, nested loop, which then runs to completion.

Can you have 3 nested loops?

In the simple cases, yes. In more complex cases, when loop indexes start at numbers indicated by other indexes, the calculations are more complex.

Can we use two for loops simultaneously?

We write code line by line for readability. So, we can write two for loops in a code and it is called as nested for loop.


2 Answers

With range/v3, you may do

auto xs = ranges::view::iota(xstart, xend);
auto ys = ranges::view::iota(ystart, yend);
auto zs = ranges::view::iota(zstart, zend);
for (const auto& point : ranges::view::cartesian_product(xs, ys, zs)){
    function_doing_stuff(point);
}
like image 103
Jarod42 Avatar answered Oct 28 '22 15:10

Jarod42


You can introduce your own class as

class myClass {
  public:
    myClass (int x, int y, int z):m_x(x) , m_y(y), m_z(z){};
  private: 
    int m_x, m_y, m_z;

}

and then initialize a std::vector<myClass> with your triple loop

std::vector<myClass> myVec;
myVec.reserve((xend-xstart)*(yend-ystart)*(zend-zstart)); // alloc memory only once;
for (int x = ystart; x < xend; x++){
    for (int y = xstart; y < yend; y++){ // I assume you have a copy paste error here
        for (int z = zstart; z < zend; z++){
            myVec.push_back({x,y,z})
        }
    }
}

Finally, you can use all the nice std algorithms with the std::vector<myClass> myVec. With the syntactic sugar

using MyRange = std::vector<MyClass>;

and

MyRange makeMyRange(int xstart, int xend, int ystart, int yend, int zstart,int zend) {
    MyRange myVec;
    // loop from above
    return MyRange;
}

you can write

const MyRange range = makeMyRange(xstart, xend, ystart, yend, zstart, zend);
for (auto point : range){
    function_doing_stuff(point);
}

With the new move semantics this wont create unneeded copies. Please note, that the interface to this function is rather bad. Perhaps rather use 3 pairs of int, denoting the x,y,z interval.

Perhaps you change the names to something meaningful (e.g.myClass could be Point).

like image 35
schorsch312 Avatar answered Oct 28 '22 15:10

schorsch312