Приветствую Вас ГостьВторник, 14.05.2024, 11:59

Каталог статей


Стив Макконнелл "Совершенный код". Циклы

Циклы сложны для понимания. Сохраняя их простыми, вы помогаете читателям вашего кода.

Цикл for

Применяйте циклы for в простых случаях, не требующих управления изнутри тела цикла.

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

Не используйте цикл for, если цикл while подходит больше.

Нормальные циклы с выходом

Цикл с выходом обычно состоит из начала цикла, тела цикла (включая условие выхода) и конца цикла:

Пример обычного цикла с выходом (Visual Basic)
Do
   //Операторы.
   ...
   If ( some exit condition ) Then Exit Do
   //Еще операторы.
   ...
Loop

Пример дублированного кода, который подвержен ошибкам при сопровождении (C++)
// Вычисляем счет и рейтинги.
score = 0;
//Эти строки появляются здесь...
GetNextRating( &ratingIncrement );
rating = rating + ratingIncrement;

while ( ( score < targetScore ) && ( ratingIncrement != 0 ) ) {
   GetNextScore( &scoreIncrement );
   score = score + scoreIncrement;
   //…и повторяются здесь.
   GetNextRating( &ratingIncrement );
   rating = rating + ratingIncrement;

}

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

// Вычисляем счет и рейтинги. Этот код использует бесконечный цикл
// и оператор break для имитации цикла с выходом.
score = 0;
while ( true ) {
   GetNextRating( &ratingIncrement );
   rating = rating + ratingIncrement;

   ///Это условие выхода из цикла (и теперь оно может быть упрощено с помощью теорем ДеМоргана)
   if ( !( ( score < targetScore ) && ( ratingIncrement != 0 ) ) ) {
      break;
   }
   GetNextScore( &scoreIncrement );
   score = score + scoreIncrement;
}

При использовании этого типа циклов учитывайте следующие тонкие моменты:

  • Разместите все условия выхода в одном месте.
  • Пишите комментарии для ясности.
  • Размещайте вход в цикл только в одном месте
  • Размещайте инициализационный код непосредственно перед циклом

Общие рекомендации по оформлению циклов

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

Избегайте пустых циклов. Пример пустого цикла (C++)
while ( ( inputChar = dataFile.GetChar() ) != CharType_Eof ) {
   ;
}

Здесь тело цикла пустое, потому что само выражение while делает две вещи: выполняет циклические действия (inputChar = dataFile.GetChar()) и проверяет, завершить ли работу цикла (inputChar != CharType_Eof). Цикл будет яснее, если его перекодировать так, чтобы его работа была более очевидной читателю:

do {
   inputChar = dataFile.GetChar();
} while ( inputChar != CharType_Eof );

Располагайте служебные операции либо в начале, либо в конце цикла Служебные операции цикла — это выражения вроде i = i + 1 или j++, чье основное назначение не выполнять работу в цикле, а управлять циклом.

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

Завершение цикла

Убедитесь, что выполнение цикла закончилось

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

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

Пример кода, который неправильно применяет последнее значение индекса цикла (C++)
for ( recordCount = 0; recordCount < MAX_RECORDS; recordCount++ ) {
   if ( entry[ recordCount ] == testValue ) {
      break;
   }
}
// Много кода
...
//Здесь неправильное применение завершающего значения индекса цикла.
if ( recordCount < MAX_RECORDS ) {
   return( true );
}
else {
   return( false );
}

Пример кода, который не делает ошибки при использовании последнего значения индекса цикла (C++)
found = false;
for ( recordCount = 0; recordCount < MAX_RECORDS; recordCount++ ) {
if ( entry[ recordCount ] == testValue ) {
   found = true;
   break;
   }
}
// Много кода
...
return( found );

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

Досрочное завершение цикла

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

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

Используйте continue для проверок в начале цикла Хорошим применением оператора continue будет перемещение операций в конец тела цикла после проверки некоторого условия в его начале.

Используйте операторы break и continue очень осторожно

Использование переменных цикла

Используйте порядковые или перечислимые типы для границ массивов и циклов Обычно счетчики циклов должны быть целыми значениями. Числа с плавающей запятой плохо инкрементируются. Например, вы можете прибавить 1,0 к 26 742 897,0 и получить 26 742 897,0 вместо 26 742 898,0. Если это число используется как индекс цикла, вы получите бесконечный цикл.

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

for ( int i = 0; i < numPayCodes; i++ ) {
   for ( int j = 0; j < 12; j++ ) {
      for ( int k = 0; k < numDivisions; k++ ) {
         sum = sum + transaction[ j ][ i ][ k ];
      }
   }
}

лучше написать

for ( int payCodeIdx = 0; payCodeIdx < numPayCodes; payCodeIdx++ ) {
   for (int month = 0; month < 12; month++ ) {
      for ( int divisionIdx = 0; divisionIdx < numDivisions; divisionIdx++ ) {
         sum = sum + transaction[ month ][ payCodeIdx ][ divisionIdx ];
      }
   }
}

Используйте смысловые имена во избежание пересечения индексов Привычное использование переменных i, j и k приводит к увеличению риска пересечения индексов — использованию одного и того же имени индекса для разных целей

Ограничивайте видимость переменных-индексов цикла самим циклом

Пример объявления переменной-индекса цикла внутри цикла for (C++)
for ( int recordCount = 0; recordCount < MAX_RECORDS; recordCount++ ) {
   // Циклический код, использующий recordCount.
}

Насколько длинным может быть цикл?

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

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

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

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

Простое создание цикла — изнутри наружу

Если у вас иногда возникают затруднения при кодировании сложного цикла (что бывает у большинства программистов), есть простой способ реализовать его с первого раза. Вот как это сделать. Начните с одного действия. Закодируйте его с помощью констант. Затем сделайте отступ, окружите его циклом и замените константы индексами цикла или вычисляемыми выражениями. Добавьте еще один цикл, если он нужен, и замените другие константы. Повторите процесс нужное число раз. После этого добавьте код инициализации. Так как вы начали с одного действия и двигались в сторону его обобщения, то можете рассматривать этот процесс как кодирование изнутри наружу.

 

 

 

Категория: Стив Макконнелл "Совершенный код" | Добавил: leshic (15.11.2021)
Просмотров: 239 | Рейтинг: 0.0/0
Всего комментариев: 0
Имя *:
Email *:
Код *:
Вход на сайт
Поиск
Категории раздела
Стив Макконнелл "Совершенный код" [20]
Стив Макконнелл "Совершенный код"
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0