【问题标题】:How to refactor nested for loop?如何重构嵌套的for循环?
【发布时间】:2021-11-17 14:12:47
【问题描述】:

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

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

【问题讨论】:

  • 在这种情况下尺寸是多少?内部函数中是否需要 i 和 j?
  • 您没有告诉我们有关调用funcA()funcB() 的不同用例的更多信息,在哪种情况下调用其中一个?基本上你想要一个参数来将该代码合并到一个函数中,并决定是否需要调用func_2()
  • for(int i = 0; i &lt; size * size; ++i) {func_1();} ?

标签: c++ algorithm function for-loop refactoring


【解决方案1】:

你可以使用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

【讨论】:

  • 而不是使用可变参数模板,最好使用 lambda 并将这些索引传递给它。
  • 感谢您的回答。我接受 lambda 函数来解决我的问题,因为我对此更熟悉。再次感谢。
【解决方案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 可能溢出或为负时,嵌套循环和平面循环之间存在差异。虽然传递可调用对象是正交的。

【讨论】:

  • “当size*size 可能溢出时,嵌套循环和平面循环之间存在差异。” 或者当size 为负数时。 ^_^
  • @Jarod42 不错的收获
  • unsigned 不会发生... (signed/unsigned 战争为size) ;-)
  • @Jarod42 我只想添加一个assert(size is not a misleading name) ;)
  • size_is_not_a_number(_as_463035818), size_is_not_a_variable ;-)
【解决方案3】:

您可以用一个template function 替换它。它接受函数的可变参数,每个函数将被fold expression(自)扩展调用。 此外,使用ranges::views::iota(自)您可以将两个for-loop 合并为一个。

如下:

#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)

【讨论】:

  • 非常感谢 JeJo 的回答并请编辑我的问题。我接受 lambda 函数来解决我的问题,因为我对此比较熟悉。
【解决方案4】:

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

【讨论】:

  • 在这种情况下,我会将 bool 设为模板参数,以便编译器可以更轻松地优化掉内部循环中的 if 语句
【解决方案5】:

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

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

【讨论】:

    【解决方案6】:

    可能会创建一个带有参数的函数,具体取决于该参数调用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() 时运行。

    【讨论】:

      【解决方案7】:

      除了在其他帖子中提到的将实现作为函数参数(例如通过 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
      

      【讨论】:

      • 一个很好的答案。谢谢。
      猜你喜欢
      • 1970-01-01
      • 2020-07-02
      • 1970-01-01
      • 2016-05-03
      • 1970-01-01
      • 1970-01-01
      • 2011-10-22
      • 2021-03-03
      • 1970-01-01
      相关资源
      最近更新 更多