Можно ли выйти из «до» в C ++, если достигнуто конечное условие? - PullRequest
7 голосов
/ 06 января 2009

Я хочу знать, возможно ли завершить цикл for в C ++, когда проверяется конечное условие (отличное от правильного числа повторений). Например:

for (int i = 0; i < maxi; ++i)
    for (int j = 0; j < maxj; ++j)
        // But if i == 4 < maxi AND j == 3 < maxj, 
        // then jump out of the two nested loops.

Я знаю, что это возможно в Perl со следующими вызовами LABEL или последними вызовами LABEL и помеченными блоками, возможно ли это сделать в C ++ или мне следует использовать цикл while?

Спасибо.

Ответы [ 15 ]

51 голосов
/ 06 января 2009

Вы можете использовать ключевое слово return: переместить вложенный цикл в подпрограмму, вызвать подпрограмму для запуска вложенных циклов и «возвратиться» из подпрограммы для выхода из [всех] циклов.

47 голосов
/ 06 января 2009

Несмотря на аргументы "goto считается вредными", это кажется идеальным местом для goto. По сути, это то, что вы делаете в Perl. Серьезно ... рассмотрим альтернативы:

Дополнительные переменные состояния


for (int i=0; i<maxi; ++i) {
    bool leaveLoop = false;
    for (int j=0; j<maxj; ++j) {
        if (i == 4 && j == 3) {
            leaveLoop = true;
            break; // leave the inner loop
        }
    }
    if (leaveLoop) {
        break; // leave the outside loop
    }
}

Выйти за исключением


try {
    for (int i=0; i<maxi; ++i) {
        for (int j=0; j<maxj; ++j) {
            if (i == 4 && j == 3) {
                throw leave_loop();
            }
        }
    }
} catch (leave_loop const&) {
}

Комплексная логика


int j = 0;
for (int i=0; i<maxi && !(i==4 && j==3); ++i) {
    for (j=0; j<maxj && !(i==4 && j==3); ++j) {
        // inner loop
    }
}

goto


for (int i=0; i<maxi; ++i) {
    for (int j=0; j<maxj; ++j) {
        if (i==4 && j==3) {
            goto leave_loop;
        }
    }
}
leave_loop:

Последний менее понятен? Я не верю, что это так. Это более хрупкий? ИМХО, остальные довольно подвержены ошибкам и хрупки по сравнению с версией goto. Извините, что стою здесь на мыльнице, но меня это беспокоит некоторое время;)

Единственное, что вы должны осознавать, это то, что goto и исключения очень похожи. Они оба открывают возможность для утечки ресурсов, и что не так относиться к ним с осторожностью.

13 голосов
/ 06 января 2009

Позвольте мне сказать это настолько решительно (но вежливо ;-), насколько я могу: Конструкция for на с-подобном языке не относится к счету.

Тестовое выражение, которое определяет, может ли продолжение быть любым, что имеет отношение к цели цикла; выражение обновления не обязательно должно быть «добавить один к счетчику».

for (int i = 0, j = 0; i < maxi && j < maxj && i != 4 && j != 3;) {
    if (j < maxj) {
        ++j;
    } else {
        j = 0;
        ++i;
    }
}

будет одним (довольно произвольным) способом переписать.

Дело в том, что если установление некоторого условия является точкой целого числа, обычно можно написать цикл (используя либо while, либо for) таким образом, чтобы более явно указывать условие продолжения / завершения.

(Если бы вы могли опубликовать описание того, что на самом деле происходит, оно, вероятно, напишет что-то, что выглядит не так произвольно, как указано выше.)

8 голосов
/ 06 января 2009

Из всех приведенных выше предложений я бы избегал использования механизма try / catch, поскольку исключения должны быть зарезервированы для исключительных обстоятельств, а не для нормального потока управления.

Использование двух разрывов - это нормально, если вы можете создать второе условие соответствующим образом. Использование логического значения для этой цели также было бы хорошо, и вы могли бы даже связать его с состоянием каждого цикла for. Например:

bool exit_loops = false;
for (int a = 0; a < A && !exit_loops; ++a)
{
    for (int b = 0; b < B && !exit_loops; ++b)
    {
        if (some_condition) exit_loops = true;
    }
}

Хотя, если вы используете более двух циклов, может быть целесообразнее заключить их в функцию и просто использовать return для выхода из функции (и всех циклов тоже). С другой стороны, вы можете реорганизовать код таким образом, чтобы устранить все циклы, кроме одного, будь то путем вызова функции для выполнения кода внутреннего цикла и т. Д.

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

8 голосов
/ 06 января 2009

Вы не можете выпрыгнуть из двух циклов с помощью одной инструкции разрыва, но вы можете использовать goto для перехода из внутреннего цикла прямо наружу.

Если goto локализован и означает, что логики меньше, чем в противном случае, я думаю, что это вполне приемлемый код. Наличие дополнительных флаговых переменных или удаление переменной итератора из внутреннего цикла, чтобы вы могли сравнить ее во внешнем цикле, не облегчает понимание кода IMHO.

6 голосов
/ 06 января 2009
bool done = false;

for (int i = 0; i < maxi && !done; ++i)
    for (int j = 0; j < maxj && !done; ++j)
        if (i == 4 && i < maxi && j == 3 && j < maxj )
             done = true;
        else {
        }

Или вы могли бы просто пойти. Или нет: -)

6 голосов
/ 06 января 2009

Вы не можете так выпрыгнуть в C / C ++:

for (...)
{
  for (...)
  {
    // from here...
  }
}
// ...to here

без использования goto. Вам нужна конструкция типа:

for (...)
{
  bool
    exit = false;

  for (...)
  {
    if (do_exit)
    {
      exit = true; // or set outer loop counter to end value
      break;
    }
  }
  if (exit)
  {
    break;
  }
}

В качестве альтернативы используйте throw и catch - но это не очень хорошо, поскольку throw действительно следует использовать для исключений, а не для управления потоком.

Чистый способ - сделать внутренний цикл функцией:

bool F ()
{
  if inner loop terminates, return false else return true
}

void G ()
{
  for (...)
  {
    if (!F ())
    {
      break;
    }
  }
}
4 голосов
/ 06 января 2009

Вы можете использовать оператор goto , но это обычно считается плохой практикой.

Другой вариант - сделать что-то подобное

int i;
int j = 0;
for (i = 0; i < maxi && !(i==4 && j==3); ++i)
    for (j = 0; j < maxj && !(i==4 && j==3); ++j)
4 голосов
/ 06 января 2009
for (int i = 0; i < maxi; ++i)
{
    int j = 0;
    for (j = 0; j < maxj; ++j)
    {
         if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
             break; // exit inner loop
    }
    if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
        break; // exit outer loop
}
2 голосов
/ 06 января 2009

Чтение кода не должно походить на чтение детективной книги (которую всегда нужно выяснить) ...

пример:

Java:

iterate_rows:
for (int i = 0; i < maxi; ++i)
{       
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            break iterate_rows;
        else
            continue iterate_rows;
    }   
}

Вам не нужно выяснять, что делает брейк iterate_rows, вы просто читаете его.

C ++:

//iterate_rows:
for (int i = 0; i < maxi; ++i)
{
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            goto break_iterate_rows;
        else
            goto continue_iterate_rows;
    }

continue_iterate_rows:;
}
break_iterate_rows:;

goto break_iterate_rows это просто видимая версия break iterate_rows

Если вы ограничите свое использование goto и меток только для этого вида кода, вы не будете выяснять намерения. Ограничение использования goto и меток для этого вида кода заставит вас просто читать код, а не анализировать или выяснять его. Вас не обвинят в том, что вы злой программист.

И если вы действительно ограничите свои goto в таком коде, вы сможете выработать привычку не выяснять, что эти чертовы goto делают в вашем коде. Дополнительным преимуществом является то, что вам не нужно вводить логические значения и отслеживать их (что, imho, приводит к обнаружению кода , делая его немного нечитаемым, что противоречит самой цели избегания gotos)

приписка

Соедините эти метки с комментариями (перед циклом), к тому времени, как вы прочитаете эти строки с помощью выражения goto, вы уже знаете цель этих gotos

...