Каковы ваши советы по отслеживанию и предотвращению ошибок в циклах? - PullRequest
2 голосов
/ 18 июня 2009

Я только что нашел ... ОПЯТЬ ... ошибка в реальном времени, как показано ниже

for (int i = 0; i < length; i++)
{ //...Lots of code 
    for (int j = 0; i < length; j++)
    {
        //...Lots of code 
    }
}

Вы заметили прямо перед собой внутреннее я, которое ДОЛЖНО БЫТЬ j? Я тоже. Так что теперь я буду использовать:

for (int i = 0; i < length; i++)
{
    for (int i1 = 0; i1 < length; i1++)
    {
    }
}

Какие у вас советы по внутренним и внешним циклам while и for?

Редактировать: Спасибо за ценные ответы. При этом краткое изложение предлагаемых советов:

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

Возможно, вы захотите проверить сводку по LBushkin для следующих

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

Ответы [ 12 ]

7 голосов
/ 18 июня 2009

Не используйте i & j (или любую другую однобуквенную переменную) в качестве имен индексов. Используйте правильные имена, и вы не попадете в этот тип проблем.

6 голосов
/ 18 июня 2009

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

for (int i = 0; i < length; i++)
{
    DoSomething();
}

private void DoSomething(int outerValue)
{
    for (int i = 0; i < length; i++)
    {
        // Do something else
    }

}
4 голосов
/ 19 июня 2009

Мой главный совет (в произвольном порядке) для написания лучшего кода цикла (большая часть этого взята из превосходной книги Code Complete ):

  1. Избегайте нескольких точек выхода для петель.
  2. Используйте продолжение / перерыв экономно.
  3. Преобразовывать вложенные циклы в отдельные подпрограммы, когда это возможно.
  4. Используйте значимые имена переменных, чтобы сделать читаемыми вложенные циклы.
  5. Используйте циклы foreach (), когда это возможно, а не для циклов (i = ...).
  6. Введите цикл только из одного места. Не прыгайте в петлю с Гото. Когда-либо.
  7. Поместите код инициализации непосредственно перед циклом.
  8. Храните операторы инициализации цикла с циклом, с которым они связаны.
  9. Избегайте повторного использования переменных между не вложенными циклами. 10. Ограничьте область видимости переменных индекса цикла для самого цикла.
  10. Используйте while (true) для бесконечных циклов, а не для (;;)
  11. В языках, которые предоставляют блочные конструкции (например, '{' и '}'), используйте их, а не отступы для включения операторов цикла. Да, даже для однолинейных циклов.
  12. Избегайте пустых циклов.
  13. Избегайте размещения дел по дому в середине цикла, вместо этого поместите их в начало и / или конец.
  14. Заставить каждый цикл выполнять только одну функцию. Избегайте смешивания обязанностей в одном цикле.
  15. Сделать условия завершения цикла очевидными.
  16. Не связывайтесь с переменной индекса цикла цикла for (), чтобы завершить его.
  17. Избегайте кода, который зависит от конечного значения индексатора цикла.
  18. Подумайте об использовании счетчиков безопасности в сложных циклах - их можно проверить, чтобы убедиться, что цикл не выполняется слишком много или слишком мало раз.
  19. Используйте операторы break, когда это возможно, для завершения циклов while.
  20. Когда возможно, сделайте ваши петли достаточно короткими, чтобы просматривать все сразу.
4 голосов
/ 18 июня 2009

Для меня «запах кода» здесь - «много кода».

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

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

Может быть возможно выделить разделы «много кода» в отдельные функции / методы, чтобы уменьшить размер основной структуры - но это не всегда может быть практичным.

Кроме того, я бы сказал, что «i1» не очень хороший выбор имени переменной, поскольку это способствует «i2», «i3» и т. Д., Что на самом деле не приводит к понятному коду. Возможно, замена всех переменных цикла на что-то более значимое поможет ясности кода и уменьшит вероятность исходной ошибки.

3 голосов
/ 18 июня 2009

Это ошибка копирования-вставки, избегайте копирования-вставки.

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

2 голосов
/ 18 июня 2009

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

Во-вторых, используйте значимые имена переменных в подобных случаях. Я бы использовал i и j только в простых циклах с несколькими строками кода. Например, если вы просматриваете двумерный массив, «col» и «row» будут иметь больше смысла, облегчат чтение кода («который был?») И упростят обнаружение ошибок, подобных этой. 1003 *

2 голосов
/ 18 июня 2009

Я пришел сюда, чтобы быть умным и сказать: «Я просто пишу это правильно с первого раза». Но потом я увидел твой пример и, ну, я сам делал это слишком много раз.

Когда вам нужны подобные вложенные циклы, мое единственное решение - это быть бдительным и думать при написании кода.

Где это возможно, использование итераторов и для каждого цикла хорошо.

Кроме того, я не вижу, как предложенное вами решение будет лучше. И это тоже не выглядит так хорошо.

2 голосов
/ 18 июня 2009

используйте вашу IDE на VS, попробуйте использовать это: http://msdn.microsoft.com/en-us/library/z4c5cc9b(VS.80).aspx

образец: введите для , затем нажмите Tab Tab последовательно

1 голос
/ 18 июня 2009

Вы просто должны позаботиться о таких проблемах, волшебной пули против этого нет. Даже при «лучшем именовании» вы предлагаете время от времени терять информацию о том, является ли это N-м или (N + M) -ым уровнем вложенного цикла, и делать ошибку.

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

0 голосов
/ 18 июня 2009

Как и в этом, как и во многих других, есть несколько превосходных советов в Code Complete * Стива Макконнелла . Было бы неплохо потратить время на то, чтобы прочесть, что он скажет о создании хорошего зацикливающегося кода. У меня нет моей книги под рукой, но вся книга стоит вашего времени.

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