在循环顶部中断循环的显式条件

Explicit conditions of breaking a loop at the top of the loop

本文关键字:循环 条件 中断 顶部      更新时间:2023-09-26

我有以下循环,它循环通过required_types数组并找到链接的对象:

for (var i = 0; i < required_types.length; i++)
{
    var linked_objects =linker.linked(required_types[i].type);
}

现在,我想在找到链接了0多个对象的类型后打破循环,所以我会用一种习惯的方式这样做:

var has_linked_objects = false;
for (var i = 0; i < required_types.length; i++)
{
    var linked_objects = ctx.linker.linked(required_types[i].type);
    var has_linked_objects = linked_objects > 0;
    if (has_linked_objects)
    {
        break;
    }
}

然而,我最近读到了CodeComplete中有趣的一章,其中指出循环中存在的内容应该尽可能清楚,并在循环的开头说明。这允许读者在检查循环之前就知道所有可能的退出条件。所以现在我会这样写循环:

var has_linked_objects = false;
for (var i = 0; i < required_types.length && has_linked_objects === false; i++)
{
    var linked_objects = ctx.linker.linked(required_types[i].type);
    var has_linked_objects = linked_objects > 0;
}

第二种选择真的比第一种更好吗?如果是,为什么?

IMHO,这是个坏建议。这种情况可能会持续很长时间,不再符合我的心理"各取所需"模式。

我读过这个

for (var i = 0; i < required_types.length; i++) {
    if (has_linked_objects) {
        break;
    }
}

作为

for each element in required_types { // easy
    if(found) {
        break;
    }
}

我读了这个

for (var i = 0; i < required_types.length && has_linked_objects === false; i++) {
}

作为

哇,这是一个复杂的循环!让我仔细阅读。。。has_linked_objects何时更改?它能在循环的一次迭代中变为true并变回false吗?

我认为考虑简单的条件要容易得多:"如果发生这种情况,那么就中断",而不是跟踪循环体中的许多变量并在下一次迭代中一次测试所有内容。第二种方法可能会使编写函数的其余部分变得更加困难,因为您无法确定是哪个条件触发了循环的结束(元素是否找到?)

您可以使用类似的方法

if (required_types.some(function(cType){return ctx.linker.linked(cType) > 0;})) {
   ...
}

这突破了在第一次迭代时迭代Array,返回true。如果我必须在你提供的两个选项中进行选择,我会选择第二个(带有以下固定代码)

var has_linked_objects = false;
for (var i = 0; i < required_types.length && has_linked_objects === false; i++)
{
    has_linked_objects = ctx.linker.linked(required_types[i].type) > 0;
}

如果可能的话,您应该将所有条件都放在循环的开头。如果这是不可能的,或者如果这会使代码的可读性更低,那么您当然可以使用break。在您的情况下,循环是如此的小和简单,它可能不会产生巨大的差异。如果你真的想确保其他开发人员知道发生了什么,你仍然可以写一条注释,列出循环中的替代退出点。

// Iterates required_types and breaks on an entry with linked objects
for (var i = 0; i < required_types.length; i++)
{
    ...
    if (has_linked_objects)
    {
        break;
    }
}

如果您需要进一步使用数组中最后一个使用的元素,那么在循环头中使用第二个条件会带来另一个问题。循环结束时,i将始终比结果高1。循环结束后,您必须将i减少一。并不是说这样做很复杂,但作为一个阅读代码的人,我会困惑一两秒钟。

for (var i = 0; i < required_types.length && has_linked_objects === false; i++)
{
    ...
}
i--; // This can easily get overread or misinterpreted
var obj_with_linked_objects = required_types[i];

TL/DR:在这种情况下,我个人更喜欢break