Можно ли выйти из a for before time в C++, если достигнуто конечное условие?

StackOverflow https://stackoverflow.com/questions/416464

  •  03-07-2019
  •  | 
  •  

Вопрос

Я хочу знать, можно ли завершить цикл 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?

Спасибо.

Это было полезно?

Решение

Вы можете использовать ключевое слово 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 на с-подобном языке не относится к счету.

Тестовое выражение, определяющее, продолжать ли, может быть чем-либо, что имеет отношение к цели цикла; выражение обновления не должно быть " добавить его в счетчик ".

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

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

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

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

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

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

Из всех приведенных выше предложений я бы не использовал механизм 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 - это плохое неструктурированное программирование, но в некоторых случаях (например, таких) они очень полезны.

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 {
        }

Или ты можешь просто пойти. Или нет: -)

Вы не можете так выпрыгнуть в 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;
    }
  }
}
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 , но это обычно считается плохой практикой.

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

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)

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

пример:

Джава:

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, вы просто читаете это.

С++:

//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:;

перейти к Break_iterate_rows это просто видимый версия сломать итерацию_строк

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

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

P.S.

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

У меня есть несколько предложений:

  1. бросать....поместите два цикла внутри «try {}», а затем «поймайте» «бросок» по условию.

  2. поместите два цикла в метод и вернитесь по условию.

  3. Гото не зло, просто люди его так используют....Вы можете использовать «goto», это может быть самый понятный код, особенно при обработке ошибок.Я не пользовался им уже 20 лет.

Тони

Я всегда старался держаться подальше от goto высказываний (по какой-то причине на меня всегда смотрели свысока в школе и на моей работе). Я бы использовал что-то вроде того, что предложил Daemin.

Вы можете использовать метки, что-то вроде:

Outer:
for(...)
{
    Inner:
    for(...)
    {
    if(condition)
        {
        goto End;
        }
    }
}
End:

Я думаю, что в Java вы можете передавать метки, чтобы сломаться?

Редактировать - изменено начало, а не конец, но не думаю, что отрицательный повтор оправдан. Этот ответ дает самый простой способ сделать это.

Другая причина полного пересмотра конструкции 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, чтобы определить, было ли найдено нужное значение.

Еще одним преимуществом вышеперечисленного является гибкость. Предположим, мы теперь проинформированы о том, что значения в строках <=> являются восходящими, поэтому остаток строки не имеет значения, если встречается значение, большее чем <=>. Легко узнать, что именно нужно сделать, и где это сделать. Поскольку эта новая информация позволяет нам отказаться от текущей строки, это влияет только на внутреннее решение:

    if (target < a[i][j] || SIZE <= ++j) { // can't be in row or fallen off end
    ...

Я вижу больше новых языков (особенно функционально-ориентированных), отказавшихся от старого " count " конструкция петли; это, вероятно, хорошо, так как это побуждает нас думать о значении цикла, а не просто считать.

Лучший способ, который я видел , связан с макросами и переходами, но на самом деле это довольно приятно (ссылка на пост начинается с разговора о 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);
        }
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top