Разорвать вложенные циклы

Может ли кто-нибудь сказать мне, как разорвать основной цикл, когда у меня есть вложенные циклы?
Пример*:

/*Main loop*/
for(int y = 0; y < 100; y+=10)
{
    /*Sub loop*/
    for (int x = 0; x < 100; x += 10)
    {
        if(x == 60) 
        { 
            //Break the main loop 
        }
    }
}

*Этот код ничего не делает, это просто пример

Что я должен поставить вместо комментария «Разорвать основной цикл»? В java есть метки, которые я могу сломать (когда я устанавливаю метку для основного цикла с именем «MainLoop», я могу написать «break MainLoop;», и это будет действительным), но что я могу сделать здесь?

Спасибо за совет!


person Bankin    schedule 13.12.2011    source источник
comment
если возможно, поместите это в отдельный метод, из которого вы можете просто вернуться, что приведет к короткому замыканию циклов.   -  person Alastair Pitts    schedule 14.12.2011
comment
ПЕРЕЙТИ К! Эй, подождите, куда вы, ребята? Привет?   -  person Mike Christensen    schedule 14.12.2011


Ответы (12)


goto!

Я не понимаю этого упорного мема, в котором говорится, что goto считается «вредным». При правильном использовании он очень мощный, и это именно тот случай.

person fge    schedule 13.12.2011
comment
Однажды, когда кто-то даст вам код тридцатилетней давности, изобилующий плохо написанными GOTO, и попросит вас исправить ошибки, и вы обнаружите, что его невозможно рефакторить, вы поймете. Лучшие программисты могут творить чудеса с помощью GOTO. Худшее может причинить большое зло. - person Caleb Hattingh; 14.12.2011
comment
Лучшие программисты могут использовать goto, не вызывая никаких проблем, и в процессе создавать наиболее эффективный код. - person Matt Lacey; 14.12.2011
comment
+1, разрыв вложенных циклов - одно из немногих (единственных?) приемлемых применений goto imo. Гораздо чище, чем использование флага. - person Adam Rackis; 14.12.2011
comment
@cjrh Вот почему он сказал Хорошо используется, это очень мощно, и это как раз тот случай.. Правильный инструмент для правильной ситуации. - person Rob; 14.12.2011
comment
Я просто не понимаю, почему некоторые люди здесь предпочитают использовать флаг, а не goto в этом случае. - person bevacqua; 05.01.2012

  • рефакторинг, поэтому вам не нужно выходить из вложенных циклов таким образом.
    Использование return часто возможно, если поместить циклы в отдельную функцию.
  • используйте goto.
  • Использовать флаг (некрасиво)
person CodesInChaos    schedule 13.12.2011
comment
Никто никогда не должен использовать goto в языке высокого уровня. - person Jon Egeland; 14.12.2011
comment
IMO flag-break-checkflag-break еще уродливее, чем goto. Но, конечно, рефакторинг является предпочтительным выбором там, где это возможно. - person CodesInChaos; 14.12.2011
comment
goto вполне законен, и это лучшее его применение, оно более эффективно, чем любой из других представленных вариантов. Ни один программист не должен жить по таким правилам, как никогда не использовать goto, вместо этого вы должны знать, когда использовать goto. - person Matt Lacey; 14.12.2011
comment
@IAbstract Это exactly тип ситуации, когда goto является лучшим ответом. Перестаньте изрыгать never use goto вздор, которому вас учили. Используйте правильный инструмент в правильной ситуации. - person Rob; 14.12.2011
comment
@Rob: правильный ответ - рефакторинг. Вариант flag-break-checkflag-break мне тоже не нравится... и дело не в регургитации, а в правильности. :) - person IAbstract; 14.12.2011

Используйте флаг, чтобы сигнализировать о завершении:

for(int y = 0; y < 100; y+=10)
{
     bool flag = false;
     for(int x = 0; x < 100; x += 10)
     {
        if (x == 56)
        {
           flag = true;
           break;
        }
     }

     if(flag) break;
}
person Tudor    schedule 13.12.2011
comment
Это подробный способ сделать это. Вы можете просто проверить флаг в условном цикле for. - person Jon Egeland; 14.12.2011
comment
Хорошая вещь в том, что это очень легко понять для других программистов: нулевой фактор неожиданности. - person Caleb Hattingh; 14.12.2011
comment
@Jon, я не думаю, что это чистый стиль - иметь более одного условия внутри for. - person Tudor; 14.12.2011
comment
Я предпочитаю goto этому обходному пути. (Конечно, только если рефакторинг не является хорошим выбором) - person CodesInChaos; 14.12.2011
comment
@cjrh ты действительно так думаешь? Я, например, думаю, что goto здесь гораздо более очевиден, не говоря уже о том, что он более эффективен. - person fge; 14.12.2011
comment
@fge: tbh, когда циклы for требуют завершения раньше, чем предполагают их пределы, я подозреваю, что это должно быть исключительным условием, если вы понимаете, что я имею в виду. С этой точки зрения goto и исключения делают одно и то же. - person Caleb Hattingh; 14.12.2011
comment
Это делается с помощью таких флагов в PASCAL. Я думал, что в C# может быть что-то более умное, как в Java. Но я буду использовать это. - person Bankin; 15.12.2011

Некоторые люди пристрелили бы меня за предложение использовать оператор goto, но выход из нескольких циклов — это одно из мест, где он может быть очень полезным (и эффективным):

/*Main loop*/
for(int y = 0; y < 100; y+=10)
{
    /*Sub loop*/
    for (int x = 0; x < 100; x += 10)
    {
        if(x == 56) 
        { 
            goto MainLoopDone;
        }
    }
}

MainLoopDone:
// carry on here
person Matt Lacey    schedule 13.12.2011
comment
Они ужасны, когда используются молодыми программистами и приводят к спагетти-коду. Как разработчик, вы должны использовать доступные вам инструменты, и goto — это наиболее эффективный способ выхода из нескольких вложенных циклов. - person Matt Lacey; 14.12.2011
comment
Я согласен с предыдущим кодом спагетти, я всегда использую break или return - person MethodMan; 14.12.2011
comment
+1 - Я с вами - это одно из немногих мест, где goto делает лучше, а не хуже. Реструктуризация, чтобы избежать вложенных циклов, была бы идеальной, но если это не вариант, то goto прочь! - person Adam Rackis; 14.12.2011
comment
Простое решение Хогана для дополнительной логической переменной почти столь же эффективно, гораздо удобнее и понятнее для других программистов. - person Brian Deragon; 14.12.2011
comment
Я не вижу, как это более ремонтопригодно. Если бы было задействовано больше циклов, вам пришлось бы изменить больше кода, также для этого не требуется дополнительная переменная. - person Matt Lacey; 14.12.2011
comment
Можно немного уточнить? Вам нужно опубликовать обновленную версию вашего кода, чтобы мы могли увидеть, что происходит. - person Matt Lacey; 15.12.2011

часто лучше поместить это в отдельную функцию, а затем выполнить «возврат»

void loop_de_loop()
{
  for(int y = 0; y < 100; y+=10)
  {
      /*Sub loop*/
      for (int x = 0; x < 100; x += 10)
      {
          if(x == 56) 
          { 
              return;
          }
      }
  }
}
person Keith Nicholas    schedule 13.12.2011
comment
В настоящих вложенных циклах я что-то вычисляю. Если мне придется передать их методу, это будет очень уродливо! - person Bankin; 15.12.2011
comment
зависит от того, как вы это сделаете.... часто эти вещи становятся беспорядочными, потому что у людей есть много переменных и они не инкапсулированы в структуру. В идеале вы получите множество функций, которые работают со структурой. (упрощенное ОО). - person Keith Nicholas; 15.12.2011

Я не знаю, есть ли способ выйти из вложенных циклов в C#, но позвольте мне предложить обходной путь.

Вы можете бросить основной цикл в функцию и вернуться из этой функции. Вы можете return false; указать преждевременный разрыв и return true; указать, что цикл прошел полностью, если это имеет значение.

person Platinum Azure    schedule 13.12.2011

Флаги, как предложено в комментариях, вероятно, лучший метод:

boolean someFlag = true;

for(int y = 0; i < 100 && someFlag; y += 10) {
  for(int x = 0; x < 100 && somFlag; x += 10) {
    if(x == 56)
      someFlag = false;
  }
}
person Jon Egeland    schedule 13.12.2011
comment
Это только потому, что после внутреннего цикла и после if нет кода. С этим кодом вам нужно добавить два разрыва и проверку. - person CodesInChaos; 14.12.2011

Нет хорошего универсального ответа. «Правильный путь» зависит от реальной проблемы. Лучший способ — поместить внешний цикл в функцию, а затем использовать return;, чтобы выйти из него. Это может быть x=100; y=100;. Это может быть done=true;. Черт возьми, это может быть даже goto (шучу).

person David Schwartz    schedule 13.12.2011

Не рекомендуется, но вы можете использовать goto. См. это.

public class GotoTest1
{
    static void Main()
    {
        int x = 200, y = 4;
        int count = 0;
        string[,] array = new string[x, y];

        // Initialize the array:
        for (int i = 0; i < x; i++)

            for (int j = 0; j < y; j++)
                array[i, j] = (++count).ToString();

        // Read input:
        Console.Write("Enter the number to search for: ");

        // Input a string:
        string myNumber = Console.ReadLine();

        // Search:
        for (int i = 0; i < x; i++)
        {
            for (int j = 0; j < y; j++)
            {
                if (array[i, j].Equals(myNumber))
                {
                    goto Found;
                }
            }
        }

        Console.WriteLine("The number {0} was not found.", myNumber);
        goto Finish;

    Found:
        Console.WriteLine("The number {0} is found.", myNumber);

    Finish:
        Console.WriteLine("End of search.");


        // Keep the console open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/*
Sample Input: 44

Sample Output
Enter the number to search for: 44
The number 44 is found.
End of search.
*/

Не рекомендуется, потому что это затрудняет понимание потока. Другой вариант, конечно, - установить какой-то флаг во внутреннем цикле и проверить его во внешнем цикле, я не принял во внимание это, поскольку это очевидно, и предполагаю, что вы все равно это знаете.. :)

person Kashyap    schedule 13.12.2011

Поскольку, как вы упомянули, в команде break нет меток, вы можете сделать что-то вроде этого:

/*Main loop*/
bool fFound;
for(int y = 0; y < 100 && !fFound; y+=10)
{
    /*Sub loop*/
    for (int x = 0; x < 100; x += 10)
    {
        if(x == 56) 
        { 
            //Break the main loop 
            fFound = true;
            break; //Break inner loop
        }
    }
}
person Mike Christensen    schedule 13.12.2011

Как уже говорили другие, «правильный» ответ зависит от проблемы, которую вы решаете. Если вы можете, лучше разбить его на более мелкие части. Что-то по этой модели:

object MainLoop ()
{
    object result = null;
    for(int y = 0; y < 100; y+=10)
    {
        result = SubLoop(y);
        if (result != null)
        {
            break;
        }
    }
    return result;
}

object SubLoop (int y)
{
    object result = null;
    for (int x = 0; x < 100; x += 10)
    {
        if(x == 56)
        {
            result = objectInstance;
            break;
        }
    }
    return result;
}

На мой взгляд, в разной степени некрасиво иметь несколько операторов return из одной функции, использовать дополнительные флаги или (вздрагивать) использовать операторы goto. Но иногда один из них необходим.

Редактировать: Это демонстрирует использование этого метода для возврата полезного объекта какого-либо типа, вы должны иметь "Customer" или "IDataAccess" или "bool" или что-то отличное от "object" в качестве возвращаемых типов, если используя это по-настоящему.

person That Chuck Guy    schedule 13.12.2011

Сначала я использовал LINQ для сбора интересных объектов, а затем выполнял операции с результатами запроса LINQ. Тем самым удаляя вложенные циклы и заменяя их одним циклом.

person flobadob    schedule 27.08.2015