'<' против '! =' как условие в цикле 'for'?

31

Скажем, у вас есть следующий forцикл *:

for (int i = 0; i < 10; ++i) {
    // ...
}

который обычно можно записать так:

for (int i = 0; i != 10; ++i) {
    // ...
}

Конечные результаты одинаковы, так есть ли реальные аргументы для использования одного над другим? Лично я использую первое в случае, если iпо какой-то причине выходит из строя и пропускает значение 10.

* Извините за использование магических чисел, но это всего лишь пример.

gablin
источник
18
int i = 10; while(i --> 0) { /* stuff */ }
Лучшее
@glowcoder оператор стрелка мой любимый
Карсон Майерс
3
@glowcoder, хорошо, но это проходит сзади. Возможно, вы не всегда этого хотите.
2
@SuperElectric, это анализирует какwhile ((i--) > 0)
Кристофер Джонсон
17
Существует (возможно, апокрифическая) история о промышленной аварии, вызванной циклическим тестированием на входе датчика! = MAX_TEMP. К сожалению, однажды сенсорный вход изменился с менее чем MAX_TEMP до более чем MAX_TEMP без каждого прохождения через MAX_TEMP. Процесс перегрелся, не обнаружив, и возник пожар. Иногда есть разница между! = И <.
Чарльз Грант

Ответы:

59

Причина выбора одного или другого из-за намерения, и в результате этого, это увеличивает удобочитаемость .

Намерение: цикл должен выполняться до тех пор, пока iон меньше 10, а не до тех пор, пока iон не равен 10. Несмотря на то, что последний может быть одинаковым в данном конкретном случае, это не то, что вы имеете в виду, поэтому он не должен быть написано так

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

Декард
источник
3
Нет, цикл не должен работать до тех пор, пока iон «меньше 10», он должен выполняться до тех пор, пока (в этом случае) верхняя граница не достигнута. При использовании C ++ интервал / итератора диапазон обоснование, это гораздо более логично , чтобы выразить это через !=чем <.
Конрад Рудольф
14
@ Конрад, я не согласен с этим вообще. Суть не в том, чтобы быть догматичным, а в том, чтобы выбрать конструкцию, которая наилучшим образом выражает цель условия. И поэтому, если вы решите циклически проходить что-то, начиная с 0 и двигаясь вверх, то <это точно выражается.
Deckard
6
@ Конрад, ты упускаешь суть. Оператор приращения в этом цикле делает очевидным, что условие цикла - это верхняя граница, а не тождественное сравнение. Если бы вы уменьшали, это было бы нижней границей. Если вы перебираете неупорядоченную коллекцию, то тождество может быть правильным условием.
Алекс Фейнман
3
@ Алекс увеличение не было моей целью. Интервал даже не обязательно имеет порядок. Он просто перебирает ... ну, кое-что, пока не достигнет конца. Например, элементы в хэш-наборе. В C ++ это делается с использованием итераторов и forцикла. Использовать <здесь было бы глупо. Кроме того, Deckard, кажется, признает это. Я очень согласен с его комментарием в ответ на мой.
Конрад Рудольф
1
Обратите внимание, что если вы используете поворотный буфер с указателями чейза, вы ДОЛЖНЫ использовать!=
SF.
48

<Картина , как правило , работает даже если приращение происходит не быть 1 точно.

Это позволяет использовать один общий способ выполнения циклов независимо от того, как это делается на самом деле.


источник
6
Это также позволяет iсовершенно безопасно модифицировать внутри цикла.
Мэтью Шарли
1
или если «i» изменено совершенно небезопасно ... У другой команды была странная проблема с сервером. Он постоянно сообщал об использовании процессора на 100%, и это должно быть проблемой с сервером или системой мониторинга, верно? Нет, я нашел условие цикла, написанное «опытным старшим программистом» с той же проблемой, о которой мы говорим.
JQA
12
За исключением того, что не все циклы C ++ for могут использовать <, такие как итераторы над последовательностью, и поэтому !=это будет единственный распространенный способ создания циклов в C ++.
Дэвид Торнли
@SnOrfus: я не совсем разбираю этот комментарий. В общем случае вы не получите надежных исключений для слишком большого увеличения итератора (хотя есть и более конкретные ситуации, в которых вы это сделаете).
Дэвид Торнли
Ну, я следую <шаблону, потому что он требует одного нажатия клавиши вместо двух с >шаблоном и четырех с !=. Это вопрос эффективности, подобной ОКР.
Спойк
21

В C ++ рекомендация Скотта Майерса в «Более эффективном C ++» (пункт 6) всегда заключается в использовании второго, если только у вас нет для этого причин, потому что это означает, что у вас одинаковый синтаксис для итерационных и целочисленных индексов, поэтому вы можете легко переключаться между intитератором без каких-либо изменений в синтаксисе.

На других языках это не применимо, так что я думаю, что <это, вероятно, предпочтительнее из-за мнения Торбьерна Равна Андерсена.

Кстати, на днях я обсуждал это с другим разработчиком , и он сказал , что причина предпочесть <более !=потому , что iможет случайно увеличивается на более чем один, и это может вызвать состояние перерыв не должны быть выполнены; это ИМО груз чепухи.


источник
17
"груз бессмыслицы" ... до того дня, когда у вас случайно не появится дополнительный i ++ в теле цикла.
2
+1, особенно за «груз чепухи», потому что это так. Если тело цикла случайно увеличивает счетчик, у вас возникают гораздо большие проблемы.
Конрад Рудольф
12
@B Тайлер, мы всего лишь люди, и большие ошибки уже случались раньше. Считайте <более безопасным программирование, чем !=тогда ...
2
@ Thorbjørn Ravn Andersen - я не говорю, что не согласен с вами, я согласен; <является более оборонительным, и мои коллеги ненавидят это, если я пишу, !=поэтому я не Тем не менее, есть случай для !=C ++, и это то, что я представил.
9
Один из сценариев, в котором можно получить случайное дополнение, i++- это изменение цикла while в цикл for. Не уверен, что иметь половину числа итераций лучше, чем (почти) бесконечный цикл; последнее, вероятно, сделает ошибку более очевидной.
ak2
12

Использование (я <10), на мой взгляд, более безопасная практика. Он улавливает максимальное количество возможных случаев выхода из программы - все, что больше или равно 10. Сравните это с другим случаем (i! = 10); это ловит только один возможный случай отказа - когда мне ровно 10.

Побочным продуктом этого является то, что он улучшает читабельность. Кроме того, если приращение будет чем-то другим 1, это может помочь минимизировать вероятность проблемы, если мы допустим ошибку при написании прекратить работу.

Рассмотреть возможность:

1) for (i = 1; i < 14; i+=2)

2) for (i = 1; i != 14; i+=2)

Хотя оба случая, скорее всего, ошибочны / неправильны, второй, скорее всего, будет БОЛЬШЕ неправильным, поскольку он не выйдет. Первый случай завершится, и существует более высокая вероятность того, что он выйдет в нужном месте, даже если 14, вероятно, неправильное число (15, вероятно, будет лучше).

Спарки
источник
Первый случай может быть правильным! Если вы хотите перебрать все натуральные числа, меньшие 14, лучшего способа выразить это не существует - вычисление «правильной» верхней границы (13) было бы просто глупо.
Maaartinus
9

Вот еще один ответ, который, кажется, еще никто не придумал. forЦиклы следует использовать, когда вам нужно перебрать последовательность . Использование !=является наиболее кратким методом определения условия завершения цикла. Однако использование менее строгого оператора является очень распространенной идиомой защитного программирования. Для целых чисел это не имеет значения - это просто личный выбор без более конкретного примера. Цикл по коллекциям с итераторами, которые вы хотите использовать !=по причинам, указанным другими. Если вы считаете последовательности floatили double, то вы хотите избежать !=любой ценой.

Я хотел бы отметить, что forон используется, когда вам нужно перебрать последовательность. Сгенерированная последовательность имеет начальную точку, интервал и условие завершения. Они кратко указаны в forзаявлении. Если вы обнаружите, что либо (1) не включает шаговую часть forили (2), указав что-то вроде trueусловия охраны, то вам не следует использовать forцикл!

whileЦикл используются для продолжения обработки в то время как состояние конкретного удовлетворяются. Если вы не обрабатываете последовательность, то, скорее всего, вам нужен whileцикл. Аргументы охранного условия здесь аналогичны, но решение между a whileи forциклом должно быть очень осознанным. whileЦикл недооцениваемый в кругах C ++ ИМА.

Если вы обрабатываете коллекцию элементов (очень распространенное forиспользование -loop), то вам действительно следует использовать более специализированный метод. К сожалению, std::for_eachэто довольно болезненно в C ++ по ряду причин. Во многих случаях отделение тела forпетли в отдельно стоящей функции (хотя и несколько болезненно) приводит к гораздо более чистому решению. При работе с коллекциями, рассмотрим std::for_each, std::transformили std::accumulate. Реализация многих алгоритмов становится лаконичной и кристально ясной, когда выражается таким образом. Не говоря уже о том, что выделение тела цикла в отдельную функцию / метод заставляет вас сосредоточиться на алгоритме, его входных требованиях и результатах.

Если вы используете Java, Python, Ruby или даже C ++ 0x , вы должны использовать правильный цикл foreach для коллекции . По мере того как компиляторы C ++ реализуют эту функцию, ряд forциклов исчезнет, ​​как и обсуждения этого типа.

D.Shawley
источник
2
«Однако использование менее ограничительного оператора - очень распространенная идиома оборонительного программирования». Это подводит итог более или менее.
Джеймс П.
+1 для обсуждения различий в намерениях по сравнению с while, forи foreach(или языковой эквивалент)
Кевин Пено
Строго говоря, цикл используется для продолжения обработки до тех пор , пока состояние конкретного будет не встретилоwhile
Альнитак
@Alnitak - Д'оо ... Я исправлю это :)
D.Shawley
Меня смутили два возможных значения «менее ограничительного»: это может означать, что оператор проявляет снисходительность в значениях, которые он передает ( <), или оператор, снисходительный в значениях, которые он терпит неудачу ( !=).
Матеин Улхак,
4

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

Стив
источник
2

Одна из причин , почему я выступает за less thanбольше not equals, чтобы действовать в качестве охранника. В некоторых ограниченных обстоятельствах (плохое программирование или дезинфекция) они not equalsмогут быть пропущены, тогда как less thanони все еще будут действовать.

Вот один пример, когда отсутствие проверки очистки привело к странным результатам: http://www.michaeleisen.org/blog/?p=358

Джеймс П.
источник
0

Вот одна из причин, почему вы предпочитаете использовать, <а не !=. Если вы используете язык с глобальной областью видимости переменных, что произойдет, если другой код изменится i? Если вы используете <вместо !=, то худшее, что происходит, - это то, что итерация заканчивается быстрее: возможно, какой-то другой код увеличивается iслучайно, и вы пропускаете несколько итераций в цикле for. Но что произойдет, если вы выполняете цикл от 0 до 10, и цикл становится равным 9, и по какой- iто странной причине некоторые плохо записанные потоки увеличиваются . Затем ваш цикл завершает эту итерацию и увеличивает ее iдо значения 11. Теперь, когда цикл пропустил условие выхода ( iникогда не равное 10), он теперь будет выполняться бесконечно.

Это не обязательно должно быть особенно причудливой логикой типа threading-and-global-variable, которая вызывает это. Возможно, вы просто пишете цикл, который необходимо откатить назад. Если вы мутируете iв цикле и запутываете свою логику, то, имея ее так, чтобы она имела верхнюю границу, а не a, !=вы вряд ли оставите вас в бесконечном цикле.

Том Моррис
источник
3
Конечно, это кажется идеальным аргументом для циклов for-each и более функциональным стилем программирования в целом. Но зачем вам это делать, когда изменяемые переменные гораздо веселее ?!
Том Моррис
3
Я не думаю, что это ужасно веская причина. В любом случае у вас есть ошибка, которую нужно найти и исправить, но бесконечный цикл имеет тенденцию делать ошибку довольно очевидной.
ak2
0

Во встроенном мире, особенно в шумной среде, вы не можете рассчитывать на то, что ОЗУ будет вести себя должным образом. В условном (для, в то время как, если), где вы сравниваете, используя '==' или '! =', Вы всегда рискуете, что ваши переменные пропустили это критическое значение, которое завершает цикл - это может иметь катастрофические последствия - Mars Lander уровень последствий. Использование «<» или «>» в ​​условии обеспечивает дополнительный уровень безопасности для обнаружения «неизвестных неизвестных».

В исходном примере, если бы по iнеобъяснимым причинам катапультировалось до значения, намного превышающего 10, сравнение «<» сразу же уловит ошибку и выйдет из цикла, но «! =» Продолжит считать, пока не iобернется вокруг 0 ​​и обратно. до 10.

oosterwal
источник
2
Еще один связанный вариант существует с кодом, например, for (i=4; i<length; i++) csum+=dat[i];где допустимые пакеты всегда будут иметь lengthпо меньшей мере четыре, но один может получить поврежденные пакеты. Если разные типы пакетов имеют разные требования к длине, может потребоваться проверить длину как часть обработки, которая отличается для каждого типа пакета, но сначала проверьте контрольную сумму как часть общей логики. Если получен пакет недостаточной длины, на самом деле не имеет значения, сообщает ли вычисление контрольной суммы «хорошо» или «плохо», но это не должно привести к сбою.
суперкат