如何分解嵌套的循环?

How do I factor out nested for loops?

本文关键字:嵌套 循环 分解 何分解      更新时间:2023-10-16

很多时候,我需要对一组类数据进行批量操作。请考虑以下类:

#include <vector>

class Component {
public:
bool isFixed;
int a, b, c;
Component():
isFixed(false),
a(0), b(0), c(0)
{}
};

class System {
public:
std::vector<Component> components;
System(int numComponents):
components(numComponents)
{}
};

class Universe {
public:
std::vector<System> systems;
Universe(int numSystems, int numComponents):
systems(numSystems, System(numComponents))
{}
};

现在,对Universe中的每个Component执行单个操作变成了循环遍历Universe中所有System的所有Component的问题:嵌套for循环。

// Fixes a Component.
//
void fixComponent(Component& c) {c.isFixed = true;}

// Adds a number to the pieces of a Component.
//
void addToComponent(Component& cmp, double k)
{
cmp.a += k;
cmp.b += k;
cmp.c += k;
}

// Fixes all components in a Universe.
//
void fixAllComponents(Universe& universe)
{
for(std::size_t i = 0; i < universe.systems.size(); ++i) {
System& thisSystem = universe.systems.at(i);
for(std::size_t j = 0; j < thisSystem.components.size(); ++j) {
fixComponent(thisSystem.components.at(j));
}
}
}

// Adds a number to all components in a Universe.
//
void addToAllComponents(Universe& universe, double k)
{
for(std::size_t i = 0; i < universe.systems.size(); ++i) {
System& thisSystem = universe.systems.at(i);
for(std::size_t j = 0; j < thisSystem.components.size(); ++j) {
addToComponent(thisSystem.components.at(j), k);
}
}
}

编写迭代for循环两次是可以的,但我可以轻松地对这些数据执行 20 个不同的任务,每次都必须重写双for。不用说,这可能很容易出错。如果我能以某种方式重用迭代代码并只专注于不同的单个任务,那就太好了。

问题在迭代集合时,是否有一种标准方法可以"分解"for循环?

尝试经过一番思考,我决定编写一个接受 2 个参数的函数,一个包含要迭代的数据的对象和一个指向执行任务的函数的指针。

void forEachComponent(Universe& u, void (*task)(Component&))
{
for(std::size_t i = 0; i < u.systems.size(); ++i) {
System& thisSystem = u.systems.at(i);
for(std::size_t j = 0; j < thisSystem.components.size(); ++j) {
task(thisSystem.components.at(j));
}
}
}

现在,如果我想修复一个组件,我可以简单地调用forEachComponent()并传入要执行的任务。

Universe theUniverse(20, 30);
forEachComponent(theUniverse, fixComponent);

这个"解决方案"的明显问题是,对于每个涉及不同参数的任务,如addToComponent(),我必须编写另一个函数,该函数将指针指向具有这些参数的函数,这违背了分解for循环的目的。

您可以使用函子重用循环访问组件的逻辑。

template <typename Functor >
void forEachComponent(Universe& universe, Functor functor)
{
for(std::size_t i = 0; i < universe.systems.size(); ++i) {
System& thisSystem = universe.systems.at(i);
for(std::size_t j = 0; j < thisSystem.components.size(); ++j) {
functor(thisSystem.components.at(j));
}
}
}
// Fixes all components in a Universe.
void fixAllComponents(Universe& universe)
{
forEachComponent(universe, [](Component& c) {fixComponent(c);});
}

// Adds a number to all components in a Universe.
void addToAllComponents(Universe& universe, double k)
{
forEachComponent(universe, [k](Component& c) {addToComponent(c, k);});
}

您可以使用范围for循环来简化forEachComponent

template <typename Functor >
void forEachComponent(Universe& universe, Functor functor)
{
for(System& system : universe.systems) {
for(Component& c : system.components) {
functor(c);
}
}
}

OOP 的方法是定义一个抽象类:

class Task {
void execute(Component &) = 0;
}

现在,您可以将forEachComponent()定义为

void forEachComponent(Universe& u, Task& task)

并在 for 循环中调用task.execute()

或者,您可以将forEachComponet()定义为模板:

template <class Task>
void forEachComponent(Universe& u, Task& task)

现在,传入此函数的任何内容都必须覆盖operator()