干净的编码:如何从循环内的函数调用中打破循环

Clean coding: how to break loops from inside a function call inside a loop

干净的编码原则通常包括函数必须小且用途单一的规则。

摘自 Robert Martin 的书 Clean Code:“函数的第一条规则是它们应该很小。函数的第二条规则是它们应该比那小。

如果我有一个复杂循环的函数,其中包含可以中断循环的分支,这将很难坚持。例如,国际象棋变体中的这段代码旨在允许一个单位通过跳过棋盘上的另一个单位来进行攻击:

for (arow = row, acol = col - 1, found_obstacle = false; acol >= 0; --acol) {
  if (!found_obstacle) {
    if (cellHasUnit(arow, acol)) found_obstacle = true;
    continue;
  } else {
    if (cellHasUnit(arow, acol)) {
      if (!cellHasAlly(arow, acol))
        _dangerzones.insert(std::make_pair(arow, acol));
      break;
    }
  }
}

我知道您不能从函数内部中断循环,因为函数是在该循环内调用的。

有什么好方法可以处理循环内复杂的中断条件,以保持函数简洁的代码?我可以想象使用一个带有 return 值的特殊函数来指示是否需要中断,但这仍然意味着每个中断都需要自己的函数。如果我有很多中断条件,这意味着包含主循环的函数仍然会很大。

编辑:我问的是一般性问题,即模块化具有多个中断条件的循环内代码是否实用和可取(从干净的编码角度来看)。

也许在 break; 语句中,设置 acol = -1 然后 continue; 以便在下一次迭代时跳出循环?

作为一个有15年以上多种编程语言编程经验的程序员,我可以首先告诉你,你带来的报价非常好,你应该按照它来制作模块化代码,但这并不意味着每个函数应该是 10 行代码。那是不可能的。

关于你的代码,没问题。不复杂。您在循环内使用函数,它看起来是模块化的。休息一下也可以。

不过,我有一个意见,使用 continue 看起来多余。你可以这样做:

if (cellHasUnit(arow, acol)) {
    found_obstacle = true;
else {
    ...

有些人完全不鼓励 continue,因为它会造成混淆。我不遵循这个建议,有时使用 continue 但我确实尽量避免在同一个循环中同时使用 breakcontinue,因为它们的含义有些相反。

您可能会发现这种形式更具表现力。它反转测试,因此避免了 cellHasUnit 测试的 2 个调用站点:

#include <map>

std::map<int, int> _dangerzones;
bool cellHasUnit(int, int);
bool cellHasAlly(int, int);

void handleAlly(int arow, int acol)
{
  if (!cellHasAlly(arow, acol))
    _dangerzones.insert(std::make_pair(arow, acol));
}

void test(int row, int col)
{
    int arow = row;
    bool found_obstacle = false;
    for (int acol = col - 1 ; acol >= 0 ; --acol) 
    {
        if (cellHasUnit(arow, acol)) 
        {
            if (found_obstacle) 
            {
                return handleAlly(arow, acol);
            }
            else  // not strictly necessary
                found_obstacle = true;
        }
    }
}

return语句用来表示在这个例子中,循环的中断也必然是测试函数的结束。

如果真正的函数更长,那么你可以这样写:

            if (found_obstacle) 
            {
                handleAlly(arow, acol);
                break;
            }
            else ...