如何重构嵌套的for循环?

How to refactor nested for loop?

我有两个类似的功能。这两个函数都包含一个嵌套的 for 循环。我如何结合这两个功能来减少重复代码。

funcAfuncB 之间的唯一区别是 funcB 在循环中调用 func_2

下面两个函数。

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();
        }
    }
}

funcA 和 funcB 的唯一区别是 funcB 调用 func_2(); 然后使用标志来控制是否应该调用该方法

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();
        }     
    }
  }
}

你本可以使用 variadic templates.

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

调用函数的方法如下。

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

您可以编写一个接受函数作为参数的函数,但没有更多信息,这是我能给出的唯一示例:

#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;
}

也许可以创建一个带参数的函数,具体取决于该参数调用 func_2()。您还可以为该参数指定一个默认值。

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() 将 运行 不调用 func_2() 并且 funcA(true) 将 运行 执行 func_2().

也许我有点过头了,但是嵌套循环没有明显的原因(func_1()func_2() 都不依赖于 ij).传递可调用对象的直接方法如下:

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

然后调用

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

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

PS:当size*size可以溢出或为负数时,嵌套循环和平面循环之间存在差异。尽管传递可调用对象与此正交。

您可以用一个 template function. It takes variadic arguments of functions, and each function will be called by the fold expression (Since ) 扩展替换它。 此外,使用 ranges::views::iota (Since ) 您可以将两个 for 循环合并为一个。

类似如下:

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

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

您将调用 funcAB:

funcAB(func1);        // for funcA() call
funcAB(func1, func2); // for funcB() call

(Live Demo)

除了其他帖子中提到的将实现作为函数参数传递的不同方式(例如通过 lambda 或函数指针)之外,您还可以考虑继承和覆盖来表达行为的重用:

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();
}

输出:

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