Home > Blockchain >  How to refactor nested loop?
How to refactor nested loop?

Time:09-24

In C , I have two similar functions. Both functions contains a nested loop, how I can conbine this two function to decrease duplicated code. The two functions like below.

void funcA() {
  for(int i = 0; i < size; i  ) {
    for(int j = 0; j < size; j  ) {
      func_1();
    }
  }
}

void funcB() {
  for(int i = 0; i < size; i  ) {
    for(int j = 0; j < size; j  ) {
      func_1();
      func_2();
    }
  }
}

The only difference between funcA and func B is funcB calls func_2 in the loop.

CodePudding user response:

You could have used variadic templates.

template<class ... FuncTypes>
void funcAB(FuncTypes... Funcs)
{
    for(int i = 0; i < size; i  ) {
        for(int j = 0; j < size; j  ) {
            (Funcs(), ...);
        }
    }
}

Here is how to call the function.

funcAB(&func_1); // if you want to only call func_1
funcAB(&func_1, &func_2) // if you want both to be called

CodePudding user response:

Perhaps I am taking it a little too far, but there is no apparent reason for the nested loops (neither func_1() nor func_2() depend on i or j). A straight forward way to pass a callable is the following:

 template <typename F>
 void func(F f) {
      for (int i=0; i < size*size;   i) f();
 }

and then call either

 func([](){ func_1(); });
 func(&func_1);               // passing function pointer works as well

or

 func([](){ func_1(); func_2(); });

PS: There is a difference between the nested loops and the flat one when size*size can overflow or it is negative. Though passing the callable is orthogonal to that.

CodePudding user response:

the only difference between funcA and funcB is that funcB calls func_2(); then use a flag to control whether that method should be called or not

void funcC(bool shouldInvokeFunc2)
{
  for(int i = 0; i < size; i  )
  {
    for(int j = 0; j < size; j  ) {
        func_1();
        if(shouldInvokeFunc2) // use the flag
        {
            func_2();
        }     
    }
  }
}

CodePudding user response:

You can write a function which accepts a function as argument but without more information this is the only example I can give:

#include <iostream>

constexpr int size = 1;

void func(void (*f)()) 
{
  for(int i = 0; i < size; i  ) 
  {
    for(int j = 0; j < size; j  ) 
    {
      f();
    }
  }
}

void func_1(){ std::cout << "func_1" << std::endl; }

void func_2(){ std::cout << "func_2" << std::endl; }

void funcA()
{
    func_1();
}

void funcB() 
{
    func_1();
    func_2();
}

int main()
{
    func(funcA);
    func(funcB);

    return 0;
}

CodePudding user response:

Maybe create one function with a parameter, depending on that parameter call func_2(). You can also give that parameter a default value.

void funcA(bool callFunc2 = false) {
  for(int i = 0; i < size; i  ) {
    for(int j = 0; j < size; j  ) {
      func_1();

      if (callFunc2) {
         func_2();
      }
    }
  }
}

funcA() will run without calling func_2() and funcA(true) will run with the execution of func_2().

CodePudding user response:

You can replace it with one single template function, which has variadic arguments, and it expands the fold expression.

In addition, using the ranges::views::iota (Since ) you can combine the two for-loops to one.

Something like follows:

#include <ranges> // std::views::iota


template<typename... Funcs>
void funcAB(Funcs&&... funcs)
{
    for ([[maybe_unused]] int const i : std::views::iota(0, size * size))
    {
        (funcs(), ...);                
    }
}

(Live Demo)

CodePudding user response:

Besides different ways of passing implementations as function arguments (e.g. through lambdas or function pointers) as mentioned in the other posts, you could think of inheritance and overriding for expressing the reuse of behaviour:

struct Base {
    int size = 2;
    void loop() {
      for(int i = 0; i < size; i  ) {
        for(int j = 0; j < size; j  ) {
            func();
        }
      }
    }
    virtual void func() = 0;
};

struct A : public Base {
    void func() override {
      cout << "for an A, call func1" << std::endl;
    }
};

struct B : public Base {
    void func() override {
      cout << "for a B, call func1 and func2" << std::endl;
    }
};

int main() {
    A a;
    B b;
    
    a.loop();
    b.loop();
}

Output:

for an A, call func1
for an A, call func1
for an A, call func1
for an A, call func1
for a B, call func1 and func2
for a B, call func1 and func2
for a B, call func1 and func2
for a B, call func1 and func2
  • Related