Вопрос о стиле существующего фрагмента кода (C / C ++) - PullRequest
7 голосов
/ 26 мая 2009

Я просто надеюсь, что следующее не кажется вам лишним jabber:)
Во всяком случае, есть что:

for (p = fmt; *p; p++) {
    if (*p != '%') {
        putchar(*p);
        continue;
    }
    switch (*++p) {
        /* Some cases here */
        ...
    }
 }

И мне стало интересно, почему писатель (Керниган / Ричи) использовал continue в выражении if.
Я думал, что только по той причине, что он посчитал, что это будет более изящно, чем отступ всего 1001 * под else утверждением, что вы думаете?

Ответы [ 11 ]

15 голосов
/ 26 мая 2009

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

Точно так же я обычно предпочитаю это:

bool foo(int arg)
{
    if(!arg) {
        /* arg can't be 0 */
        return false; 
    }

    /* Do some work */
    return true;
 }

К этому:

 bool foo(int arg) 
 { 
     if(!arg) {
         /* arg can't be 0 */ 
         return false; 
     } else {
         /* Do some work */ 
         return true;
     } 
 }

Или, хуже того:

bool foo(int arg) 
{ 
    if(arg) {
        /* Do some work */ 
        return true;
    } else {
        /* arg can't be 0 */ 
        return false; 
    } 
}

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

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

Кроме того, как отмечали другие, оператор continue дает понять, что нет необходимости читать дальше код внутри цикла, чтобы определить, выполняется ли какая-либо дополнительная обработка после этой точки для этого случая, что облегчает выполнение кода. Опять же, чем меньше вы заставляете читателя отслеживать, тем лучше.

9 голосов
/ 26 мая 2009

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

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


Например:

if(arg1 == NULL)
  return;

if(arg2 == NULL)
  return;

//Do some stuff

против

if(arg1 != null)
{
  if(arg2 != null)
  {
    //Do some stuff
  }
}
2 голосов
/ 26 мая 2009

Я думаю, что у него было бы достаточно оснований для отступа кода под переключателем, а отступ всего содержимого функции довольно бесполезен для горизонтального пространства. В то время, когда код был написан, я думаю, что 80 символов по-прежнему были популярны.

Я не думаю, что это трудно понять, но я думаю, что очень приятно упомянуть, что вы НЕ делаете немедленно, а затем GTFO.

2 голосов
/ 26 мая 2009

Намного легче читать, если это так.

Мы закончили с этой итерацией цикла? Да? Итак, давайте продолжим со следующей итерацией.

1 голос
/ 26 мая 2009

Может быть более одной причины, чтобы продолжить / разорвать цикл. Так это будет выглядеть следующим образом:

loop
{
   if (cond1)
   {
      if (cond2)
      {
         if (cond2)
         {
            more conditions...
         }
      }
   }
   else
   {
      the loop action 
   }
}

ИМХО, это не так элегантно и читабельно, как цикл в вашем примере, например:

loop
{
   if (cond1)
      continue;
   if (cond2)
      continue;
   if (cond2)
      continue;   
   if( more conditions...)
      continue;

  the loop action 
}

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

P.S. только для случая: я не думаю, что авторы думали о том, как написать этот цикл, они просто написали это:)

1 голос
/ 26 мая 2009

Наиболее вероятная причина в том, что следующий switch довольно длинный - это похоже на синтаксический анализ формата printf.

1 голос
/ 26 мая 2009

Если вы используете else, то все внутри else должно иметь отступ:

if ()
{
  doA();
}
else
{
  doB();
  if ()
  {
    doC();
  }
  else
  {
    doD()
  }
}

Если вы используете continue, то вам не нужно делать отступ:

if ()
{
  doA()
  continue;
}
doB();
if ()
{
  doC();
  continue;
}
doD();

Кроме того, continue означает, что я могу перестать думать об этом случае: например, если я увижу else, возможно, позже в цикле будет больше обработки случая '%', т.е. из заявления else; тогда как, увидев continue, я сразу узнаю, что обработка дела '%' в цикле полностью завершена.

1 голос
/ 26 мая 2009

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

1 голос
/ 26 мая 2009

Всегда есть много способов написать код, подобный этому -

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

"если значение в p не равно '%', установите и продолжайте."

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

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

0 голосов
/ 26 мая 2009

Я придерживаюсь учений Дейкстры: Гото вредно . И продолжить / перерыв - младшие братья Гото.

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

Например, фрагмент @Kamarey будет еще яснее, как это:

loop
{
   if (!(cond1 ||
         cond2 ||
         cond2 || 
         ...))
   {
      the loop actions;
   }
}

или пример @Ori Pessach можно выразить так:

bool foo(int arg)
{
    if(arg) {
        /*do some work*/
    }

    return arg != 0;
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...