Ответ 1
Вы можете использовать ключевое слово return
: переместить вложенный цикл в подпрограмму, вызвать подпрограмму для запуска вложенных циклов и "вернуть" из подпрограммы, чтобы закрыть [все] циклы.
Я хочу знать, можно ли закончить цикл for в С++, когда проверяется конечное условие (отличное от правильного количества итераций повторного набора). Например:
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 и помеченными блоками, возможно ли это сделать на С++ или я должен использовать цикл while?
Спасибо.
Вы можете использовать ключевое слово return
: переместить вложенный цикл в подпрограмму, вызвать подпрограмму для запуска вложенных циклов и "вернуть" из подпрограммы, чтобы закрыть [все] циклы.
Несмотря на "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
и исключения довольно похожи. Они оба открывают возможность утечки ресурсов и что не так относиться к ним с осторожностью.
Позвольте мне сказать это как решительно (но вежливо;-), как я могу: Конструкция for
в c-подобном языке не относится к подсчету.
Тестовое выражение, которое определяет, следует ли продолжать, может быть любым, что имеет отношение к цели цикла; выражение update не должно быть "добавить один к счетчику".
for (int i = 0, j = 0; i < maxi && j < maxj && i != 4 && j != 3;) {
if (j < maxj) {
++j;
} else {
j = 0;
++i;
}
}
будет одним (довольно произвольным) способом переписать.
Дело в том, что если установление некоторого условия является точкой взаимодействия, обычно можно написать цикл (используя либо while
, либо for
) таким образом, чтобы более явное условие условия continue/terminate.
(Если бы вы могли опубликовать описание того, что на самом деле происходит, оно, скорее всего, напишет что-то, что не выглядит столь же произвольным, как выше).
Вы не можете выпрыгнуть из двух циклов с одной инструкцией о разрыве, но вы можете использовать goto для перехода из внутреннего цикла справа.
Если goto локализован и означает, что логика меньше, чем в других, я думаю, что это вполне приемлемый код. Имея дополнительные переменные флага или поднимая переменную итератора из внутреннего цикла, чтобы вы могли сравнить его во внешнем цикле, не упрощается понимание кода IMHO.
Из всех приведенных выше предложений я бы не использовал механизм try/catch, потому что исключения должны быть зарезервированы для исключительных обстоятельств, а не для нормального потока управления.
Использование двух разрывов в порядке, если вы можете создать второе условие соответствующим образом. Использование логического для этой цели также было бы хорошим, и вы могли бы даже подключить его к условию каждого цикла. Например:
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 - плохое неструктурированное программирование, но в некоторых случаях (например, это) они очень полезны.
Вы не можете выпрыгивать, как это, в 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;
}
}
}
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 {
}
Или ты мог бы просто перейти. Или нет: -)
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
}
Вы можете использовать goto statement, но это обычно считается плохой практикой.
Другой вариант - сделать что-то вроде этого
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)
Код чтения не должен напоминать чтение детективной книги (которая всегда должна быть выяснена)...
пример:
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;
}
}
Вам не нужно выяснять, что такое break iterate_rows, вы просто прочитали его.
С++:
//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 и меток на этом типе кода заставит вас просто прочитать код, не анализируя его и не выясняя. Вы не будете обвиняться в злобном программисте.
И если вы действительно ограничите свои gotos в таком виде кода, вы сможете развить привычку не нуждаться в том, чтобы понять, что делают эти чертовы gotos в вашем коде. Добавленное преимущество - вам не нужно вводить логические значения и отслеживать их (что imho приводит к обнаружению кода, что делает его немного нечитаемым, что наносит ущерб самой цели избегать gotos)
P.S.
Соедините эти ярлыки с комментариями (перед циклом), к тому времени, когда вы прочитаете эти строки с помощью инструкции goto, вы уже знаете намерение этих gotos
Вы можете использовать метки, что-то вроде строк:
Outer:
for(...)
{
Inner:
for(...)
{
if(condition)
{
goto End;
}
}
}
End:
В Java вы можете передавать ярлыки, чтобы сломаться, я думаю?
Изменить - изменено значение goto до End, а не Outer, однако я не думаю, что отрицательный rep оправдан. Этот ответ дает самый простой способ сделать это.
У меня есть несколько предложений:
throw.... поместите две петли внутри "try {}", а затем "поймайте" "бросок" при условии.
поместите две петли в метод и вернемся к условию.
Goto не является злым, его используют люди, которые тоже его используют... Вы можете использовать "goto", это может быть самый чистый код, особенно при обработке ошибок. Я не использовал его через 20 лет.
Тони
Я всегда старался держаться подальше от заявлений goto
(по какой-то причине он всегда смотрелся в школе и моя работа). Я бы использовал что-то вроде того, что предложил Дэмин.
Еще одна причина для полного пересмотра конструкции for
заключается в том, что ее область охвата предотвращает доступ к контролируемым переменным после завершения цикла. Значение (переменные) переменной (ов), которая мутируется в цикле, может быть полезна по целому ряду причин (например, чтобы отличить успех от сбоя в поиске), который в противном случае потребовал бы дополнительных переменных для сохранения этой информации после завершения области действия. Вот небольшой пример, который ищет квадратный массив с именем a
для значения target
(при условии, что SIZE
отличен от нуля, в противном случае поиск не требуется!):
int i = 0;
int j = 0;
while (i < SIZE && a[i][j] != target) { // still in array but not at target
if (SIZE <= ++j) { // fallen off the end of a row
j = 0;
++i;
}
}
В следующем коде можно использовать i < SIZE
, чтобы определить, находилось ли требуемое значение.
Еще одно преимущество вышеизложенного - гибкость. Предположим, что теперь нам сообщили, что значения в строках a
возрастают, поэтому оставшаяся часть строки не имеет значения, если встречается значение больше, чем target
. Легко понять, какие изменения внести, и где это сделать. Поскольку эта новая информация позволяет нам отказаться от текущей строки, влияет только внутреннее решение, становясь следующим:
if (target < a[i][j] || SIZE <= ++j) { // can't be in row or fallen off end
...
Я вижу больше новых языков (особенно функционально-ориентированных), отказываясь от старой конструкции цикла counting; что, вероятно, хорошо, поскольку это побуждает нас думать о значении цикла, а не просто считать.
Лучший способ, которым я видел, включает в себя макросы и gotos, но на самом деле это довольно хорошо (связанный с сообщением начинается с обсуждения Perl, но последний абзац или около того вводит макросы).
Он позволяет вам писать код типа:
named (LOOPS) for (i=1; i<10; i++) {
for (j=1; j<10; j++) {
for (j=1; j<10; j++) {
/* Process data[i][j][k] here */
if (data[i][j][k] < threshold) break(LOOPS);
}
}
}