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

Что лучше - для цикла с прерыванием или условной петлей?

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

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

для цикла с break:

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++;
}
4b9b3361

Ответ 1

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

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

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

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

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

bool isBaxterInMilwaukee;    

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

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


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

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

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


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

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 on выход из цикла может меняться. Тем не менее, это возможно, и для некоторых людей это может улучшить читаемость:

var i:int = -1;

var isBaxterInMilwaukee:Boolean;    

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

Ответ 2

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

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

Это не эффективность работы вещь, это вещь поддерживающая.

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

Ответ 3

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

Ответ 4

В цикле 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.

Ответ 5

Существует концептуальная разница между ними. Циклы 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, довольно бессмысленна. Как вы можете оправдать бросание исключения? Это просто нелокальное и недетерминированное переключение! Кстати, это не напыщенная речь об обработке исключений, просто наблюдение.

Ответ 6

Тот, который имеет наибольший смысл, будет тем, который передает идею человеку, читающему код лучше всего. Прежде всего помните читаемость кода, и вы, как правило, сделаете правильный выбор. Как правило, вы не хотите использовать что-то вроде 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, иначе он не сработает с первого раза, если входное значение не будет таким же, как целевое значение...

Ответ 7

break считается "структурированным переходом" и его следует использовать экономно. Используйте его, если это меньше зла.

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

Изменить: Поскольку у меня появилось довольно много комментариев и некоторые downvotes, я добавлю вспомогательные ссылки

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

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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

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

  • условие while (! isBaxterInMilwaukee || я == arrayLen) - вы имели в виду "(! (isBaxterInMilwaukee || я == arrayLen))??

  • оператор break не нужен, если вы используете переменную terminate-loop.

Лично я нахожу простой "перерыв" намного легче читать, чем пытаться отслеживать переменную terminate-loop.

Ответ 11

Есть два аспекта проблемы:

  • Что делать (например: найти, если один из элементов содержит указанного человека в местоположении)
  • Как сделать это (например: использовать индекс, итерацию и т.д.)

Оба примера смешивают два и трудно понять, что из этого. Лучше всего было бы, если бы мы могли выразить в коде только ту часть. Вот пример (С# 3.5), который делает это с помощью specification pattern

// what we are looking for?
IsPersonInLocation condition = new IsPersonInLocation("baxter", "milwaukee");

// does the array contain what we are looking for?
bool found = myArray.Find(item => condition.IsSatifiedBy(item));

// do something if the condition is satisfied
if (found) {
    barkTwice();
}

Для полноты здесь приведено определение класса для условия:

class IsPersonInLocation {
    public string Person { get; set; }
    public string Location { get; set; }
    public IsPersonInLocation(string person, string location) {
        this.Person = person;
        this.Location = location;
    }
    bool IsSatifiedBy(item) {
        return item["name"] == this.Person
            && item["location"] == this.Location;
    }
}

Ответ 12

Это зависит от конкретных обстоятельств. Но в вашем примере вы хотите пройти массив ограниченной длины, и использование цикла for упрощает его выполнение и защищает от истечения конца. В примере с вашим циклом вы должны сделать свой собственный прирост - что может быть проблематичным, если вы хотите использовать оператор continue для перехода к следующему циклу - и сделать более сложное условное выражение (которое, кстати, имеет ошибку, я думаю, вы имели в виду && i != arrayLen). Вам просто нужно сделать дополнительный код, чтобы добиться эффекта, который предоставляет помощь в цикле.

Конечно, некоторые пуристы утверждают, что break и continue не должны использоваться и что вам нужно использовать if-else и логические переменные, если необходимо, а не продолжать или выходить из цикла. Но я думаю, что это может сделать цикл более уродливым, особенно если его относительно короткое и легко понять в целом, как этот пример. Для цикла с гораздо более длинным кодом, где разрыв или продолжение может легко скрыться от уведомления, пуристский подход может быть более понятным, поскольку в этом случае цикл уже усложняется. Но вы всегда можете сделать это как часть цикла for, просто добавьте его как часть условного.

Также лучше проверить массив, связанный с i < arrayLen, а не на точное равенство, если что-то заставило i пропустить точное значение (я действительно видел, что это произошло в ошибке Y2K, которая могла иметь избегали лучшей практики).

Ответ 13

У меня есть фон С++, и поэтому у меня все еще есть моменты, когда я пытаюсь "думать как компилятор". В то время как циклы, как правило, приводят к более жесткому коду, и поэтому для циклов только когда-либо учитывались, если вы знали, что каждый раз перебираете каждый элемент в массиве.

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

Ответ 14

Моя теория заключается в том, что есть полезная абстракция программирования, подобная "отношение сигнал/шум", что является "соотношением" проблема-инструмент "- доброта может быть измерена в одном измерении тем, сколько времени я трачу на размышления о проблема и ее решение, по сравнению с тем временем, которое я трачу, думая о том, как использовать инструмент (в данном случае синтаксис языка).

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

И хорошо иметь все, что вам нужно знать (grokwise) о правилах циклов в одной строке в верхней части цикла "за". Я также склонен сначала поставить переключатель "по умолчанию" в тестах по той же причине.

Но согласованность и ясность - это размышление о верховности. YMMV, конечно.

Ответ 15

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

В JS:

if(myArray.some(function(o) { o.name == "baxter" && o.location == "milwaukee" }))
  barkTwice();

или с некоторыми утилитами вашего собственного

if(myArray.containsMatch({name:"baxter",location:"milwaukee"})
  barkTwice();

Ответ 16

Инкапсулируйте цикл в свой собственный метод и используйте обработку возврата к завершению, когда условие соответствия выполнено.

Пример кода С#:

class Program
{
   static bool IsBaxterInMilwaukee(IList<WhoAndWhere> peopleAndPlaces)
   {
      foreach (WhoAndWhere personAndPlace in peopleAndPlaces)
      {
         if (personAndPlace.Name == "Baxter" 
            && personAndPlace.Location == "Milwaukee")
         {
            return true;
         }
      }
      return false;
   }

   static void Main(string[] args)
   {
      List<WhoAndWhere> somePeopleAndPlaces = new List<WhoAndWhere>();
      somePeopleAndPlaces.Add(new WhoAndWhere("Fred", "Vancouver"));
      somePeopleAndPlaces.Add(new WhoAndWhere("Baxter", "Milwaukee"));
      somePeopleAndPlaces.Add(new WhoAndWhere("George", "London"));

      if (IsBaxterInMilwaukee(somePeopleAndPlaces))
      {
         // BarkTwice()
         Console.WriteLine("Bark twice");
      }
   }

   public class WhoAndWhere
   {
      public WhoAndWhere(string name, string location)
      {
         this.Name = name;
         this.Location = location;
      }

      public string Name { get; private set; }
      public string Location { get; private set; }
   }

}

Ответ 17

Моя общая позиция:

Если он использует счетчик циклов для() (например, while while while).

Ответ 18

Я проголосую while, потому что breaks уменьшают grokability.

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

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