Какие у вас есть советы по отслеживанию и предотвращению ошибок в циклах?

StackOverflow https://stackoverflow.com/questions/1011712

  •  06-07-2019
  •  | 
  •  

Вопрос

Я только что нашел...СНОВА ...ошибка с потерей реального времени следующим образом

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

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

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

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

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

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

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

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

Решение

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

Другие советы

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

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

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

}

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

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

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

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

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

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

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

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

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

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

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

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

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

Там, где это возможно, полезно использовать итераторы и циклы foreach.

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

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

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

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

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

Я использую «ii» и «jj» для счетчиков переходных циклов, если они мне действительно нужны — их легче искать, чем «i» и «j», а также их легче обнаружить в примерах, подобных приведенным выше.Чтобы пойти еще дальше, вы можете использовать реальное имя переменной.Если вы перебираете строку, вы можете назвать ееcharacterIndex или что-то в этом роде.Это больше печатать, но оно документирует себя и экономит время на отладке неясных проблем в дальнейшем.

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

Наконец, если возможно, было бы неплохо полностью отказаться от цикла: Повышение::Foreach — это один из способов сделать это в C++, хотя я обычно предпочитаю использовать такие языки, как Python, которые изначально допускают прямую итерацию по содержимому контейнера без необходимости увеличения значения индекса или итератора.

Попробуйте использовать больше декларативных конструкций цикла.Например, если вам на самом деле не нужны индексы (те iпесок js) и ваша среда программирования позволяет это, вы можете использовать foreach конструкция для перебора коллекции.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top