Руководство: в то время как против - PullRequest
6 голосов
/ 21 октября 2009

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

В C ++ есть две выдающиеся структуры цикла: while и for.

  • Я намеренно игнорирую конструкцию do ... while, она не имеет себе равных
  • Я знаю std::for_each и BOOST_FOREACH, но не каждый цикл является for each

Теперь я могу быть немного стеснен, но мне всегда хочется исправить код, подобный этому:

int i = 0;
while ( i < 5)
{
  // do stuff
  ++i; // I'm kind and use prefix notation... though I usually witness postfix
}

И преобразовать его в:

for (int i = 0; i < 5; ++i)
{
  // do stuff
}

Преимущества for в этом примере несколько, на мой взгляд:

  1. Населенный пункт : переменная i живет только в области действия цикла
  2. Pack : цикл 'control' упакован, поэтому, взглянув только на объявление цикла, я могу понять, правильно ли он сформирован (и будет ли он завершен ...), при условии, конечно, что цикл переменная больше не изменяется внутри тела
  3. Это может быть встроено, хотя я бы не всегда советовал (что делает для хитрых ошибок)

У меня есть склонность поэтому не использовать while, за исключением, возможно, идиомы while(true), но это не то, что я использовал некоторое время (каламбур). Даже для сложных условий я склонен придерживаться конструкции for, хотя в нескольких строках:

// I am also a fan of typedefs
for (const_iterator it = myVector.begin(), end = myVector.end();
     it != end && isValid(*it);
     ++it)
{ /* do stuff */ }

Конечно, вы можете сделать это с while, но тогда (1) и (2) не будут проверены.

Я хотел бы избежать «субъективных» замечаний (типа «мне нравится за / пока лучше»), и мне определенно интересны ссылки на существующие руководства по кодированию / стандарты кодирования.

РЕДАКТИРОВАТЬ :

Я склонен придерживаться (1) и (2), насколько это возможно, (1), потому что рекомендуется локальность >> Стандарты кодирования C ++: пункт 18 , и (2), потому что это делает обслуживание проще, если мне не нужно сканировать весь цикл тела, чтобы найти возможные изменения управляющей переменной (что я принимаю как должное, используя for, когда 3-е выражение ссылается на переменные цикла).

Однако, как показано ниже gf, его можно использовать:

while (obj.advance()) {}

Обратите внимание, что это не скандал против while, а скорее попытка найти, какой из while или for использовать, в зависимости от конкретного случая (и по веским причинам, а не просто от симпатии).

Ответы [ 9 ]

7 голосов
/ 21 октября 2009

Не все циклы предназначены для итерации:

while(condition) // read e.g.: while condition holds
{
}

в порядке, хотя это выглядит принудительно:

for(;condition;)
{
}

Вы часто видите это для любых входных источников.

У вас также может быть неявная итерация:

while(obj.advance())
{
}

Опять же, это выглядит принудительно с помощью for.

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

for(A a(0); foo.valid(); b+=x); // a and b don't relate to loop-control
4 голосов
/ 21 октября 2009

Функционально, это одно и то же, конечно.Единственная причина для дифференциации - это придание некоторого значения сопровождающему или другому человеку, читающему / рецензирующему код.

Я думаю, что идиома while полезна для сообщения читателю кода, что нелинейныйtest контролирует цикл, в то время как a for idiom обычно подразумевает некоторую последовательность.Мой мозг также вроде «ожидает», что циклы for управляются только секцией подсчета выражений аргументов оператора for, и я удивлен (и разочарован), когда обнаружил, что кто-то условно возился с переменной index внутри блока выполнения.

В стандарте кодирования можно указать, что циклы «for» должны использоваться только при соблюдении конструкции цикла full: индекс должен быть инициализирован в разделе инициализатора, индекс должен быть проверен в цикле.раздел теста, и значение индекса должно быть изменено только в разделе выражения подсчета.Любой код, который хочет изменить индекс в исполняющем блоке, должен использовать вместо этого конструкцию while.Обоснованием этого может быть «вы можете доверять циклу for для выполнения, используя только те условия, которые вы видите, без необходимости искать скрытые операторы, которые изменяют индекс, но вы не можете предполагать, что в цикле while что-то верно».

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

2 голосов
/ 21 октября 2009

Одна из самых красивых вещей в C ++ - это часть алгоритмов STL. При правильном чтении кода, написанного с использованием STL, программист будет читать высокоуровневые циклы вместо низкоуровневые циклы .

2 голосов
/ 21 октября 2009

я не увеличивается автоматически в течение цикла.

while (i < 5) {

   // do something with X

   if (X) {
       i++;
   }

}
1 голос
/ 21 октября 2009

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

Как вы заметили, петля for - это просто более компактный способ сказать

type counter = 0;
while ( counter != end_value )
{
  // do stuff
  ++counter;
}

Хотя его синтаксис достаточно гибок, чтобы позволить вам делать с ним другие вещи, я стараюсь ограничить использование for примерами, которые не намного сложнее, чем приведенные выше. ОТО, я бы не использовал цикл while для вышеперечисленного.

0 голосов
/ 21 октября 2009

Я бы не стал так быстро выбрасывать циклы "do-while". Они полезны, если вы знаете, что ваше тело цикла будет запущено хотя бы один раз. Рассмотрим некоторый код, который создает один поток на ядро ​​процессора. С циклом for это может выглядеть так:

for (int i = 0; i < number_of_cores; ++i)
    start_thread(i);

В начале цикла первое, что проверяется, это условие, в случае если number_of_cores равно 0, и в этом случае тело цикла никогда не должно запускаться. Подождите, хотя - это абсолютно лишняя проверка! У пользователя всегда будет хотя бы одно ядро, иначе как работает текущий код? Компилятор не может исключить первое избыточное сравнение, насколько он знает, number_of_cores может быть 0. Но программист знает лучше. Итак, исключаем первое сравнение:

int i = 0;
do {
    start_thread(i++);
} while (i < number_of_cores);

Теперь при каждом запуске этого цикла на одноядерном компьютере вместо двух выполняется только одно сравнение (для цикла for условие истинно для i = 0, ложно для i = 1, тогда как do while равно false все время). Первое сравнение опущено, поэтому оно быстрее. С меньшим потенциалом ветвления меньше вероятность ошибочных прогнозов ветвления, поэтому оно быстрее. Поскольку условие while теперь более предсказуемо (всегда ложно для 1-ядра), предсказатель ветвлений может выполнять работу лучше, а это быстрее.

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

0 голосов
/ 21 октября 2009

В Ye Olde C циклы for и while не были одинаковыми.

Разница состояла в том, что в циклах for компилятор был свободен назначать переменную в регистр ЦП и восстанавливать регистр после цикла. Таким образом, подобный код имел неопределенное поведение:

int i;
for (i = 0; i < N; i++) {
    if (f(i)) break;
}

printf("%d", i);           /* Non-defined behaviour, unexpected results ! */

Я не уверен на 100%, но я верю, что это описано в K & R

Это нормально:

int i = 0;
while (i < N) {
    if (f(i)) break;
    i++;
}
printf("%d", i);

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

0 голосов
/ 21 октября 2009
// I am also a fan of typedefs
for (const_iterator it = myVector.begin(), end = myVector.end();
     it != end && isValid(*it);
     ++it)
{ /* do stuff */ }

Мне кажется, что приведенный выше код менее читабелен, чем приведенный ниже.

// I am also a fan of typedefs
const_iterator it = myVector.begin();
end = myVector.end();
while(it != end && isValid(*it))
{
    /* do stuff */ 
    ++it}

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

0 голосов
/ 21 октября 2009

Я склонен использовать циклы for, когда идет какой-то подсчет, и цикл заканчивается, когда отсчет заканчивается. Очевидно, у вас есть стандартный for( i=0; i < maxvalue; i++ ), но есть и такие вещи, как for( iterator.first(); !iterator.is_done(); iterator.next() ).

Я использую циклы while, когда неясно, сколько раз цикл может повторяться, т. Е. «Цикл до тех пор, пока не выполнится какое-либо условие, которое не может быть предварительно вычислено (или не выполнено)».

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