Что лучше - для цикла с разрывом или условного цикла? - PullRequest
23 голосов
/ 27 февраля 2009

Мне просто любопытно, что думают люди по этой теме. Допустим, у меня есть массив объектов, и я хочу просмотреть их, чтобы увидеть, содержат ли объекты определенные значения, и если да, я хочу остановить цикл. Что является лучшей практикой - цикл for с разрывом или условный цикл?

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

для цикла с разрывом:

var i:int;

var isBaxterInMilwaukee:Boolean;    

for (i = 0; i < arrayLen; i++)
{
    if (myArray[i]["name"] == "baxter"
         && myArray[i]["location"] == "milwaukee")
    {
        isBaxterInMilwaukee = true;

        barkTwice();

        break;
    }
}

условный цикл:

var i:int;

var isBaxterInMilwaukee:Boolean;    

while (!isBaxterInMilwaukee && i < arrayLen)
{
    if (myArray[i]["name"] == "baxter"
         && myArray[i]["location"] == "milwaukee")
    {
        isBaxterInMilwaukee = true;

        barkTwice();
    }

    i++;
}

Ответы [ 19 ]

28 голосов
/ 27 февраля 2009

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

В немного более старые времена я знаю, что выход из цикла считался запретным (наравне с оператором goto). Циклы должны были нарушаться в состоянии цикла и больше нигде. Таким образом, цикл while был бы подходящим вариантом.

(Это, вероятно, удержание от сборки, где циклы - это, по сути, блок кода с оператором перехода go-to-the-begin-if-true в конце. Несколько операторов условного перехода в блоке делают его чрезвычайно трудно отладить, поэтому их нужно было избегать и объединять в одно целое.)

Мне кажется, что эта идея сегодня немного меняется, особенно с циклами foreach и управляемым миром; это действительно вопрос стиля сейчас. Разломы для найденных циклов, возможно, стали приемлемыми для многих, за исключением, конечно, некоторых пуристов. Обратите внимание, что я все же избегал бы использования break в цикле while, поскольку это может запутать состояние цикла и сделать его запутанным.

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

bool isBaxterInMilwaukee;    

foreach (var item in myArray)
{
    if (item.name == "baxter" && item.location == "milwaukee")
    {
        isBaxterInMilwaukee = true;    
        barkTwice();
        break;
    }
}

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


Возможно, все это должно быть преобразовано в собственную функцию , которая не break при обнаружении, но на самом деле return s результат (не стесняйтесь использовать вместо этого версию for-loop ):

bool isBaxterInMilwaukee(Array myArray)
{      
    foreach (var item in myArray)
    {
        if (item.name == "baxter" && item.location == "milwaukee")
        {
            barkTwice();
            return true;
        }
    }
    return false;
}

Как указал Эско Луонтола, вероятно, было бы лучше перенести вызов на barkTwice() вне этой функции, поскольку побочный эффект не очевиден из названия функции и не связан с поиском Бакстера в каждом случае. (Или добавьте логический параметр BarkTwiceIfFound и измените строку на if(BarkTwiceIfFound) barkTwice();, чтобы прояснить побочный эффект.)


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

var i:int;

var isBaxterInMilwaukee:Boolean;    

for (i = 0; !isBaxterInMilwaukee && i < arrayLen; i++)
{
    if (myArray[i]["name"] == "baxter"
         && myArray[i]["location"] == "milwaukee")
    {
        isBaxterInMilwaukee = true;    
        barkTwice();
    }
}

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

var i:int = -1;

var isBaxterInMilwaukee:Boolean;    

while (!isBaxterInMilwaukee && ++i < arrayLen)
{
    if (myArray[i]["name"] == "baxter"
         && myArray[i]["location"] == "milwaukee")
    {
        isBaxterInMilwaukee = true;
        barkTwice();
    }
}
7 голосов
/ 27 февраля 2009

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

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

Дело не в эффективности, а в удобстве обслуживания.

Спросите своих коллег, что читается и понятно для конкретной ситуации ... Вот что действительно имеет значение.

5 голосов
/ 27 февраля 2009

Я бы сказал, это зависит. В этом случае цикл с разрывом кажется мне более понятным.

4 голосов
/ 27 февраля 2009

Между ними есть концептуальная разница. Циклы for предназначены для итерации по дискретным наборам, а циклы while предназначены для повторения операторов, основанных на условии. Другие языки добавляют в finally предложения и циклические конструкции, такие как foreach или until. У них, как правило, значительно меньше традиционных for петель.

В любом случае, я использую правило: циклы for повторяются, а циклы while повторяются. Если вы видите что-то вроде:

while (counter <= end) {
   // do really cool stuff
   ++counter;
}

Тогда вам, вероятно, лучше использовать цикл for, поскольку вы выполняете итерации. Тем не менее, такие петли, как:

for (int tryCount=0; tryCount<2; ++tryCount) {
    if (someOperation() == SUCCESS) {
       break;
    }
}

следует записать в виде while циклов, поскольку они действительно что-то повторяют до тех пор, пока условие не станет истинным.

Мысль об отказе от использования break, поскольку она так же зла, как и goto, довольно бессмысленная Как вы можете оправдать выбрасывание исключения? Это просто нелокальное и недетерминированное движение! Кстати, это не ругань против обработки исключений, а просто наблюдение.

4 голосов
/ 27 февраля 2009

В цикле for вы также можете досрочно выйти, поместив критерии досрочного выхода в объявление цикла for. Так что для вашего примера вы можете сделать это так:

var i:int;

var isBaxterInMilwaukee:Boolean;    

isBaxterInMilwaukee = false;

for (i = 0; i < arrayLen && !isBaxterInMilwaukee; i++)
{
    if (myArray[i]["name"] == "baxter"
        && myArray[i]["location"] == "milwaukee")
    {
        isBaxterInMilwaukee = true;

        barkTwice();
    }
}

Таким образом, вам не нужен разрыв, и он все же более читабелен, чем цикл while.

3 голосов
/ 27 февраля 2009

Я вижу разрыв в обеих петлях, это правильно?

В любом случае:

  • Я бы выбрал цикл FOR, когда существует известное количество (максимальное количество) итераций до начала цикла.
  • В противном случае я бы выбрал WHILE.
  • В цикле FOR я свободно использую BREAK.
  • В цикле WHILE я предпочитаю использовать сложное условие вместо BREAK (если это возможно).
3 голосов
/ 27 февраля 2009

перерыв считается «структурированным переходом» и должен использоваться с осторожностью. Используйте это, если это меньшее из зол.

Избегайте продолжайте еще больше.

Редактировать: так как я получил довольно много комментариев и несколько голосов, я добавлю несколько вспомогательных ссылок

Стандарт кодирования C / C ++

И более слабый оператор для Java (см. .52)

3 голосов
/ 27 февраля 2009

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

Вероятно, лучшее, что вам здесь нужно, это модификация вашего while цикла:

while(!isBaxterInMilwaukee || i < arrayLen) {
  if(myArray[i]["name"] == "baxter" && myArray[i]["location"] == "milwaukee") {
    isBaxterInMilwaukee == true;
    barkTwice()
  } else {
    i++;
  }
}

Это понятно и не использует break или continue, так что вы можете сразу увидеть, что вы всегда прекратите выполнение в результате одного из условий, указанных в выражении while.

ETA : Вероятно, должно быть i < arrayLen в цикле while, в противном случае он завершится неудачно с первого раза, если только входное значение не совпадает с целевым значением ...

2 голосов
/ 27 февраля 2009

Я бы определенно пошел на перерыв +. «For» - это мгновенно распознаваемая идиома «повторять последовательность», и ее легче понять «повторять последовательность»; заканчиваться раньше, если найдено значение », чем объединенное условие« петля и остановка ».

Это может быть доказано тем, что вы допустили две ошибки в коде условного цикла!

  • условие while (! IsBaxterInMilwaukee || i == arrayLen) - вы имели в виду «(! (IsBaxterInMilwaukee || i == arrayLen))»?

  • оператор break не требуется, если вы используете переменную с прерыванием цикла.

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

2 голосов
/ 27 февраля 2009

Я бы сказал, перерыв, яснее (даже если вы добавите комментарий, почему вы выходите из цикла) Imho, пока цикл не ясен, я бы пошел на перерыв

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