如何在嵌套的for循环中使用类似于continue语句的东西?

30
我有一组对象,需要将每个对象的一个属性与所有其他对象的相同属性进行比较。如果它们匹配,则代码需要执行某些操作。这导致两个“for循环”循环遍历对象以获取该属性,在第二个“for循环”中,有一个第三个“for循环”遍历属性的元素(这是一个向量)进行比较。如果它们匹配,我需要最外层的“for循环”中止当前迭代并继续进行下一个迭代(我只想考虑与另一个对象的第一个匹配)。
我已经研究了 'goto' 语句和创建 do {} while() 结构,但无法以实现所需的结果的方式实现它们。我需要的是类似于最外层循环的 'continue' 语句,具体取决于最内层循环的条件语句中发生的情况。
什么是实现此目的的好方法,并且如何实施?
for (int i = 0; i < max; i++){
Object & object1 = system.getAgent(i);
VectorOfStrings object_property1 = object1.getProperty();

    for (int j = i + 1; j < max; j++){
    Object & object2 = system.getObject(j);
    VectorOfStrings object_property2 = object2.getProperty();

        for (unsigned int k = 0; k < object_property1.size(); k++){

            if (object_property1[k] == object_property2[k]){

            //do something

            break; //this aborts the inner most loop
            //Additionally, I need the outer most loop to move on one iteration
            }
        }
    }
}

如果“k”循环中的条件语句得到满足,我希望“i”循环能够中止当前迭代并继续进行下一个迭代。

另外,由于我刚开始学习,代码可能不够优雅,请你们在回答时专注于这个具体的问题!当然,如果一般性地重构代码可能是解决问题的方法 :)


我需要最外层循环前进一次,你是指增加i吗?另外,您是否考虑过如果object_property1的大小与object_property2不同会发生什么? - stijn
附言:在我看到“我缺乏英语词汇…”之前,我一直以为你和我一样是以英语为母语的人。你的英语很好。 - Martin Bonner supports Monica
“do {...} while(false);”技巧适用于避免嵌套if-else而不是嵌套循环,因为它还使用“break”跳出switch。 - stefaanv
@stijn 是的,但它们是同一类的成员,所以它们的大小相同,这不会引起问题! - faranzki
@stefaanv 谢谢你提供的信息! - faranzki
@faranzki 确实现在可能是这种情况,但在编程中通常最好对此进行防御。如果“它们是相同的”条件发生变化,您将不得不更新所有相关代码。因此,如果您现在通过断言或计算最大索引来处理此问题,则您的代码是安全的。 - stijn
4个回答

54
这可以使用goto来实现:
for (int i = 0; i < max; i++){
    Object & object1 = system.getAgent(i);
    VectorOfStrings object_property1 = object1.getProperty();

    for (int j = i + 1; j < max; j++){
        Object & object2 = system.getObject(j);
        VectorOfStrings object_property2 = object2.getProperty();
        for (unsigned int k = 0; k < object_property1.size(); k++){
            if (object_property1[k] == object_property2[k]){
                //do something
                goto continue2; //this aborts the inner most loop
                //Additionally, I need the outer most loop to move on one iteration
            }
        }
    }
    continue2:;
}

这是一个罕见的情况,使用goto确实简化了代码。

4
我会将标签命名为类似于“property_matched”的有意义的名称。 - Martin Bonner supports Monica
1
@MartinBonner 我的意思是将 cnt 作为从 continue 衍生出的有意义的首字母缩略词。 - alexeykuzmin0
@alexeykuzmin0,这仍然不太清楚,因为它本身不能继续,所以将其改为类似于goto_continue的东西。还要注意:必须使用花括号才能使其正常工作,否则由于某种原因会破坏变量作用域x)。 - jave.web
4
除非绝对必要,否则不要使用 goto - 你可能知道自己在做什么,但是随着其他人的介入,很快就会有人使用它来完全破坏你的应用程序并可能破坏数据! - jave.web

8
你可以使用lambda表达式并使用return语句来改变控制流。如果返回true,lambda表达式将结束,并且外部的"for"循环将继续执行。
紧凑版本:
for(;;) {
    [&]() {
        for(;;) {
            if(/*break condition here*/)
                return;
        }
    }();
}

更为通用的模板:
for(;;) {
    auto innerLoop = [&]() {
        for(;;) {
            if(/*break condition here*/)
                return true;
        }
        return false;
    };

    if(innerLoop())
        continue;
}

对于这种情况:

for (int i = 0; i < max; i++){
    Object & object1 = system.getAgent(i);
    VectorOfStrings object_property1 = object1.getProperty();

    auto innerLoop = [](){
        for (int j = i + 1; j < max; j++){
        Object & object2 = system.getObject(j);
        VectorOfStrings object_property2 = object2.getProperty();

            for (unsigned int k = 0; k < object_property1.size(); k++){

                if (object_property1[k] == object_property2[k]){
                    //do something
                    return true;
                }
            }
        }
        return false;
    };

    if(innerLoop())
        continue;
}

32
聪明,但阅读起来就像由一位在服用蘑菇时有阅读障碍的痴迷者所雕刻的古代楔形文字牌匾一样费力。 - Richard Hodges
我没有测试这个,因为根据您提供的代码,我有点困惑如何实现它! 我应该在 if() 之后留空括号吗? - faranzki
这只是一个模板,加入条件以在if()中断。 - Bediver
1
另一种选择是,如果内部循环 lambda 可以有一个有意义的名称并且可以被重复使用,那么将其移动到常规函数中。 - stefaanv
2
这只是毫无意义的元编程,它并没有增加可读性。事实上,它产生了相反的效果。 - Lundin
显示剩余2条评论

8
最易读的解决嵌套循环问题的方法通常是将其放入函数中。我不知道具体代码应该做什么,但这里有一些伪代码可供参考:
bool keep_going = true;
for (int i = 0; i < max && keep_going; i++)
{
  Object& object1 = system.getAgent(i);
  keep_going = !compare_objects(object1.getProperty(), i+1, max);
}

其中compare_objects类似于以下内容:

inline bool compare_objects (const VectorOfStrings& obj_prop1, size_t begin, size_t end)
{
  for(size_t i=begin; i<end; i++)
  { 
    Object& obj2 = system.getObject(j);
    VectorOfStrings obj_prop2 = obj2.getProperty();

    for size_t j = 0; j < obj_prop1.size(); j++)
    {
      ifobj_prop1[j] == obj_prop2[j])
      {
        do_something();
        return true;
      }
    }
  }

  return false;
}

将代码外部化,这样每次循环迭代时都需要跳转到其他地方进行读取并不是“最易读”的方式。 - undefined
@Kröw 编写可读性高、易于维护的程序的关键在于将程序分解为小而可读的函数。例如,为自定义对象创建一个专用的比较函数是大多数程序中常见的功能之一。 - undefined
不,那是不正确的。函数应该封装某个特定“思想”背后的逻辑,而且当你有一组需要在多个位置重复执行的指令时,你应该使用函数。仅仅因为“太大”而将逻辑上属于同一个思想或算法的一组指令拆分开来,并不能减小代码的大小,只是将其分离开来,这样任何试图“维护”它的人现在都必须在两个不同的地方查找才能理解一个单一的思想。 - undefined
特别是在代码仅在一个地方使用的情况下,它应该放在那个地方。你可以将你的答案中的函数分割成5个其他函数,这些函数按照层次结构相互调用。但这并不会使代码更易读。试图阅读代码的人仍然需要分解每个被分割的函数中的逻辑,而如果这些逻辑仅仅在原始函数中,他们也必须这样做。 - undefined
@Kröw 一个比较函数确实是一个独立的概念。再次强调,这是一个在任何编程语言中都普遍存在的设计模式。你可以称之为函数对象、重载运算符或者等值方法 - 随便怎么称呼。反对使用比较函数就像是反对天空的颜色一样,你可以争论个不停,但这并不会改变其他人编写程序的方式。 - undefined
我只是在指出,将一件事分成两部分,然后将其中一部分移到其他地方,并不总是有助于更容易理解。我无法想象你是如何将其解释为“不要使用比较函数”的。而且我希望你没有认为我在这里的评论意图是“改变每个其他人编写程序的方式”。 - undefined

7

我强烈推荐@alexeykuzmin0的方法,但如果你必须在禁止使用goto的环境中工作,那么可以使用以下替代方法(带有烦人的标志):

for (int i = 0; i < max; i++){
    Object & object1 = system.getAgent(i);
    VectorOfStrings object_property1 = object1.getProperty();

    bool property_matched = false;
    for (int j = i + 1; j < max && !property_matched; j++){
        Object & object2 = system.getObject(j);
        VectorOfStrings object_property2 = object2.getProperty();
        for (unsigned int k = 0; k < object_property1.size(); k++){
            if (object_property1[k] == object_property2[k]){
                //do something
                property_matched = true; // This will break the "j" loop
                break; //this aborts the inner most loop
            }
        }
    }
}

1
我认为在这种情况下它并不会让人感到烦恼,因为您可能确实想知道属性是否匹配。 - stefaanv
1
@stefaanv:是的,如果你想要模拟类似于Python中的for ... else ...,那么标志就很有用了。另一方面,在标签情况下,您可以直接在中间的for循环之后放置处理“未匹配”的代码(可能需要使用continue),并在标签之后处理“匹配”的代码。 - Martin Bonner supports Monica
1
@MartinBonner 再问一个问题:虽然这个程序可以正常工作,但是我收到了编译器的警告信息:“警告:标记 ‘property_matched’ 虽已定义但未使用”。我该如何避免这种情况发生? - faranzki
1
啊,“label property_matched not used”(标签未使用)。如果您正在使用该标志,请删除该标签!您肯定不希望两者都存在。 - Martin Bonner supports Monica
1
我太傻了。在从标签选项切换到标志选项时,我忽略了它并忘记删除它。请忽略那个愚蠢的评论。 - faranzki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接