Подтвердить что ты не робот

Альтернативы `while (1)` для упрощения ветвления

Время от времени я использую блок while(1), чтобы сгладить последовательность if..else, выходящую из пропорции. Он идет по этим линиям.

Вместо этого:

// process 
if (success) {
  // process 
  if (success) {
    //process
    if (success) {
      // etc
    }
  }
}

Я делаю:

while (1) {
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
  break;
}

Меня немного раздражает неявный прыжок в конце while. Могу ли я уйти с более стройной конструкцией (т.е. Нет break в конце)?

Я мог бы обменять окончательный break на переменную (или зарегистрироваться?). Это не совсем четкое или четкое.

int once = 1;
while (once--) {
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
}

А для цикла будет выглядеть немного лучше (C99):

for (int once = 1 ; once--; once) {
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
}

Я думал об использовании корпуса коммутатора. Это выглядит не намного лучше, хотя это сработает.

switch (1) { default:
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
}

В этом конкретном случае понятие метки кажется непобедимым.

// process
if (!success) goto end;
// process
if (!success) goto end;
// process
if (!success) goto end;
// etc

end:

Какой другой подход вы знаете/используете?

4b9b3361

Ответ 1

Ниже приведен метод, очень похожий на то, что вы делаете с циклами, но без необходимости в счетчике или инструкции break в конце.

do
{
    // process
    if (!success) break;
    // process
    if (!success) break;
    // process
    if (!success) break;
    ...
    // No need for a break statement here
}
while(0);

Ответ 2

Какой другой подход вы знаете/используете?

Вы можете инкапсулировать цикл while в функцию (и вызвать эту функцию, где у вас был цикл while):

static void process(void)
{
   // process
   if (!success) return;
   // process
   if (!success) return;
   // process
   if (!success) return;
   // process
}

Любой наполовину достойный компилятор (например, даже gcc с отключенными оптимизациями) встраивает функцию static, если она вызывается один раз. (Конечно, некоторые переменные, возможно, должны быть в лексическом объеме функции process, в этом случае просто укажите их как параметры функции).

Обратите внимание, что запись кода сверху вниз, а не горизонтально (например, ваш пример с вложенным if) называется duffing. Здесь есть хорошая статья по этому вопросу:

"Чтение кода сверху вниз"

Кроме того, в стиле кодирования ядра Linux существует специальное предупреждение о горизонтальном коде:

", если вам нужно больше трех уровней отступов, вы все равно ввернуты и должны исправить вашу программу"

Ответ 3

Если вы согласны с тем, что тело каждого условного блока, генерирующего success, является следующей функцией или каждый // process в противном случае может быть сведен к булевскому выражению, например:

success = f1() ; 
if( success ) 
{
  success = f2() ; 
  if( success ) 
  {
    success = f3() ; 
    if( success ) 
    {
      success = f4()
    }
  }
}

Затем вы можете уменьшить это до одного булевского выражения, использующего оценку короткого замыкания:

success = f1() && 
          f2() && 
          f3() && 
          f4() ;

Здесь f2() не будет вызываться, если f1() возвращает значение false, и то же самое для каждой последующей оценки выражения вызова прерывания в первом под-выражении операнда операнда && для возврата false.

Ответ 4

Непонятно, зачем вам нужно гнездиться или сломаться. Я делаю это все время, когда последовательность должна выполняться с первого раза:

// process

if (success) {
  // more process
}

if (success) {
  // still more process
}

if (success) {
  // even more process
}

Ответ 5

Свернутые биты обеспечивают общий подход. Другим распространенным подходом является использование одного символа состояния/флага для достижения аналогичного результата.

bool bErr = false;

if (!bErr && success) {
   // do something
} else {
   bErr = true;
}
if (!bErr && success2) {
   // do something
} else {
   bErr = true;
}

if (bErr) {
   // hanlde cleanup from errors
}

Ответ 6

Другим вариантом будет использование простой переменной флага

bool okay = true;

if(okay &= success){
    // process
}

if(okay &= success){
    // process
}

if(okay &= success){
    // process
}