Условия завершения цикла - PullRequest
       43

Условия завершения цикла

5 голосов
/ 25 сентября 2008

Эти for -циклы являются одними из первых основных примеров формальных корректных доказательств алгоритмов. Они имеют разные, но эквивалентные условия расторжения:

1   for ( int i = 0; i != N; ++i )

2   for ( int i = 0; i < N; ++i )

Разница становится понятной в постусловиях:

  • Первый дает сильную гарантию, что i == N после завершения цикла.

  • Второй дает слабую гарантию того, что i >= N после завершения цикла, но у вас возникнет соблазн предположить, что i == N.

Если по какой-либо причине приращение ++i когда-либо изменяется на что-то вроде i += 2, или если i изменяется внутри цикла, или если N отрицательно, программа может завершиться ошибкой:

  • Первый может застрять в бесконечном цикле. Сбой рано, в цикле с ошибкой. Отладка проста.

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

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

Ответы [ 13 ]

4 голосов
/ 25 сентября 2008

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

Конечно, у него есть немного ленивое преимущество: набирать на один символ меньше;)

Я бы также сказал, что в языке с разумными правилами области видимости, поскольку я объявлен внутри конструкции цикла, он не должен быть доступен вне цикла. Это уменьшило бы любую зависимость от того, что я равен N в конце цикла ...

2 голосов
/ 25 сентября 2008

Мы не должны смотреть на счетчик изолированно - если по какой-либо причине кто-то изменит способ увеличения счетчика, он изменит условия завершения и полученную логику, если это требуется для i == N.

Я бы предпочел второе условие, так как оно более стандартное и не приведет к бесконечному циклу.

1 голос
/ 25 сентября 2008

Я думаю, что большинство программистов используют 2-й, потому что это помогает выяснить, что происходит внутри цикла. Я могу посмотреть на это и «знать», что я начну с 0 и определенно буду меньше N.

1-й вариант не имеет этого качества. Я могу посмотреть на это, и все, что я знаю, это то, что я начну с 0 и что оно никогда не будет равно N. Не совсем так полезно.

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

Конечно, второй вариант также имеет то преимущество, что это то, что используют все ссылки на C, которые я видел: -)

1 голос
/ 25 сентября 2008

Я всегда использую # 2, так как тогда вы можете быть уверены, что цикл прекратится ... Опираясь на то, что он будет равен N, впоследствии полагается на побочный эффект ... Не лучше ли вам просто использовать саму переменную N

[править] Извините ... Я имел в виду # 2

1 голос
/ 25 сентября 2008

Если вы доверяете своему коду, вы можете сделать либо.

Если вы хотите, чтобы ваш код был читабельным и легко понятным (и, следовательно, более терпимым по сравнению с кем-то, кого вы должны считать клюцем), я бы использовал что-то вроде;

for ( int i = 0 ; i >= 0 && i < N ; ++i) 
1 голос
/ 25 сентября 2008

В C ++ использование != теста является предпочтительным для общности. Итераторы в C ++ имеют различные концепции, такие как входной итератор , прямой итератор , двунаправленный итератор , итератор произвольного доступа , каждый из которых расширяет предыдущий с новыми возможностями. Для работы < требуется итератор с произвольным доступом, тогда как != просто требует входной итератор.

0 голосов
/ 17 октября 2012

С моей точки зрения формальной проверки и автоматического анализа завершения я настоятельно предпочитаю # 2 (<). Очень легко отследить, что некоторая переменная увеличивается (до var = x, после var = x+n для некоторого неотрицательного числа n). Однако не так легко увидеть, что i==N в конечном итоге имеет место. Для этого нужно сделать вывод, что i увеличивается ровно на 1 на каждом шаге, который (в более сложных примерах) может быть потерян из-за абстракции.

Если вы подумаете о цикле, который увеличивается на два (i = i + 2), эта общая идея становится более понятной. Чтобы гарантировать завершение, теперь нужно знать, что i%2 == N%2, тогда как это не имеет значения при использовании < в качестве условия.

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

Для общего программирования я предпочитаю

for ( int i = 0; i < N; ++i )

до

for ( int i = 0; i != N; ++i )

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

В этом аргументе говорилось, что "у вас будет соблазн предположить, что я == N", я не верю, что это правда Я никогда не делал этого предположения и не видел, чтобы это делал другой программист.

0 голосов
/ 25 сентября 2008

Я предпочитаю это иногда:

for (int i = 0; (i <= (n-1)); i++) { ... }

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

Другая версия:

for (int i = 1; (i <= n); i++) { ... }

помогает определить, как часто вызывается тело цикла. Это также имеет действительные варианты использования.

0 голосов
/ 25 сентября 2008

Использование любого из вышеперечисленных в c # вызовет ошибку компилятора, если вы используете i вне цикла

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