'<' versus '! =' як умова в циклі 'for'?


31

Скажіть, у вас є наступний forцикл *:

for (int i = 0; i < 10; ++i) {
    // ...
}

що це також може бути записано як:

for (int i = 0; i != 10; ++i) {
    // ...
}

Кінцеві результати однакові, тож чи існують реальні аргументи щодо використання одного над іншим? Особисто я використовую колишній у випадку, якщо iз якихось причин йде сірий пропуск і пропускає значення 10.

* Вибачте за використання магічних чисел, але це лише приклад.


18
int i = 10; while(i --> 0) { /* stuff */ }
Прекрасним

@glowcoder оператор стрілки - мій улюблений
Карсон Майєрс,

3
@glowcoder, приємно, але він проходить зі спини. Ви можете не завжди цього бажати.

2
@SuperElectric, вона розбирає, якwhile ((i--) > 0)
Крістофер Джонсон,

17
Існує (ймовірно, апокрифна) історія про виробничу аварію, спричинену тимчасовим тестуванням циклу для входу датчика! = MAX_TEMP. На жаль, одного дня вхід датчика перейшов від менше MAX_TEMP до більшого MAX_TEMP без кожного проходу через MAX_TEMP. Процес перегрівався без виявлення, і виник пожежа. Іноді існує різниця між! = І <.
Чарльз Е. Грант

Відповіді:


59

Причина вибирати те чи інше - це з наміру, і в результаті цього збільшується читабельність .

Намір: цикл повинен працювати протягом тих пір, поки iвін не перевищує 10, а не до тих пір, iпоки не дорівнює 10. Хоча останній може бути однаковим у цьому конкретному випадку, це не те, що ви маєте на увазі, тому він не повинен писати так.

Читання: результат запису того, що ви маєте на увазі, це також легше зрозуміти. Наприклад, якщо ви користуєтесь i != 10, хтось, читаючи код, може задатися питанням, чи всередині циклу існує якийсь спосіб, який iможе стати більшим, ніж 10, і що цикл повинен продовжуватися (btw: це поганий стиль, щоб возитися з ітератором деінде, ніж у голові for-statement, але це не означає , що люди не роблять цього , і в результаті супроводжують очікують його).


3
Ні, цикл не повинен працювати до тих пір, iпоки "менше 10", він повинен працювати до тих пір, поки (в даному випадку) верхня межа не буде досягнута. При використанні C ++ інтервал / ітератора діапазон обгрунтування, це набагато більш логічно , щоб висловити це через !=ніж <.
Конрад Рудольф

14
@Konrad Я взагалі не погоджуюся з цим. Основний момент - не бути догматичним, а скоріше обрати конструкцію, яка найкраще виражає наміри умови. І так, якщо ви вирішите переглядати щось, починаючи з 0 і рухаючись вгору, то <насправді це виражає точно.
Декард

6
@Konrad, ти не вистачаєш суті. Оператор приросту в цьому циклі дає зрозуміти, що стан циклу є верхньою межею, а не порівнянням ідентичності. Якби ви декрементували, це буде нижня межа. Якщо ви повторюєте невпорядковану колекцію, то ідентичність може бути правильною умовою.
Алекс Фейнман

3
@ Алекс збільшення не було моєю суттю. Інтервал навіть не обов'язково має замовлення. Це просто повторює… ну, щось, поки не досягне кінця. Наприклад, елементи в наборі хешу. У C ++ це робиться за допомогою ітераторів та forциклу. Використовувати <тут було б нерозумно. Також Декард, схоже, це визнає. Я дуже згоден з його коментарем у відповідь на моє.
Конрад Рудольф

1
Зауважте, якщо ви використовуєте поворотний буфер з покажчиками погоні, ви ОБОВ'ЯЗКОВО використовуєте!=
SF.

48

<Картина , як правило , працює навіть якщо приріст відбувається не бути 1 точно.

Це дозволяє отримати єдиний загальний спосіб робити петлі незалежно від того, як це робиться насправді.


6
Він також дозволяє iповністю модифікувати всередині циклу, якщо це необхідно.
Меттью Шарлі

1
або якщо 'я' змінено повністю небезпечно ... У іншої команди була дивна проблема з сервером. Він постійно звітував про 100% використання процесора, і це має бути проблема з сервером або системою моніторингу, правда? Ні, я знайшов умову циклу, написану "старшим експертом-програмістом" з тією ж проблемою, про яку ми говоримо.
jqa

12
За винятком того, що не всі C ++ для циклів можуть використовувати <, наприклад, ітератори над послідовністю, і так !=буде єдиним загальним способом робити петлі на C ++.
Девід Торнлі

@SnOrfus: Я не зовсім розбираю цей коментар. Взагалі ви не отримаєте надійно винятків для надмірного збільшення ітератора (хоча є і більш конкретні ситуації, коли вам доведеться).
Девід Торнлі

Ну, я слідую за <шаблоном, оскільки для цього потрібен один натискання клавіші замість двох з >малюнком, і чотири з !=. Це дійсно питання ефективності, подібної до OCD.
Спойк

21

У C ++ рекомендація Скотта Майєрса в "Ефективнішій C ++" (пункт 6) - завжди використовувати друге, якщо у вас немає причин цього не робити, оскільки це означає, що у вас є той самий синтаксис для ітератора та цілих індексів, щоб ви могли легко мінятися між intі ітератором без жодної зміни синтаксису.

В інших мовах це не стосується, тому, мабуть <, мабуть переважніше з точки зору Торбйорна Равна Андерсена.

До речі, днями я обговорював це з іншим розробником , і він сказав , що причина віддати перевагу <більш !=тому , що iможе випадково збільшується на більш ніж один, і це може викликати стан перерва не повинні бути виконані; тобто ІМО - навантаження дурниць.


17
"завантаження нісенітниці" ... до дня, коли у вас випадково з'явиться додатковий i ++ в тілі петлі.

2
+1, особливо для "завантаження дурниць", тому що це так. Якщо корпус циклу випадково збільшується лічильником, у вас набагато більші проблеми.
Конрад Рудольф

12
@B Тайлер, ми лише людина, і раніше були більші помилки. Вважайте <більш оборонним програмування, ніж !=тоді ...

2
@ Thorbjørn Равн Андерсен - Я не кажу, що я не згоден з тобою, я так роблю; <є більш захисним, і мої колеги ненавиджу це, якщо я пишу !=так, що я цього не роблю. Однак !=у C ++ є випадок, і ось що я представив.

9
Один зі сценаріїв, коли можна закінчитись випадковою додатковою ситуацією, i++- це зміни циклу часу на цикл for. Не впевнений, що наявність половини кількості ітерацій є кращою, ніж (майже) нескінченна петля; остання, мабуть, зробить помилку більш очевидною.
ak2

12

Використання (i <10) є, на мою думку, більш безпечною практикою. Він фіксує максимальну кількість потенційних випадків, що виходять із справи - все, що більше або дорівнює 10. Контраст цього випадку з іншим випадком (i! = 10); це лише один можливий випадок відмови - коли мені точно 10.

Побічним продуктом цього є те, що він покращує читабельність. Крім того, якщо приріст буде чимось іншим 1, це може допомогти мінімізувати ймовірність виникнення проблеми, якщо ми помилимось при написанні справи про вихід.

Поміркуйте:

1) for (i = 1; i < 14; i+=2)

2) for (i = 1; i != 14; i+=2)

Хоча обидва випадки, ймовірно, помилкові / неправильні, другий, ймовірно, БЕЗПЕЧНО помиляється, оскільки не вийде з ладу. Перший випадок припиниться, і є більший шанс, що він вийде в потрібному місці, хоча 14, мабуть, неправильне число (15, мабуть, буде краще).


Перший випадок може бути правильним! Якщо ви хочете перебрати всі натуральні числа менше 14, то немає кращого способу висловити це - обчислення "правильної" верхньої межі (13) було б просто дурним.
maaartinus

9

Ось ще одна відповідь, яку, схоже, ще ніхто не придумав. forпетлі слід використовувати, коли вам потрібно перебирати послідовність . Використання !=є найбільш стислим методом констатації умови завершення циклу. Однак використання менш обмежувального оператора є дуже поширеною ідіомою захисного програмування. Для цілих чисел це не має значення - це лише особистий вибір без конкретнішого прикладу. Перебираючи колекції за допомогою ітераторів, які ви хочете використовувати, !=з інших причин. Якщо ви розглядаєте послідовності floatабо double, ви хочете уникнути !=будь-якої ціни.

Що я хотів зазначити, це те, що forвикористовується, коли вам потрібно перебирати послідовність. Сформована послідовність має початкову точку, інтервал та закінчувальну умову. Вони коротко визначені в forзаяві. Якщо ви виявите, що (1) не включає ступінчасту частину forабо (2), вказуючи щось на зразок trueумови захисту, тоді вам не слід використовувати forцикл!

whileЦикл використовуються для продовження обробки в той час як стан конкретного задовольняються. Якщо ви не обробляєте послідовність, то, ймовірно, whileзамість цього ви хочете цикл. Аргументи умови збереження тут подібні, але рішення між a whileі forциклом повинно бути дуже свідомим. whileЦикл недооцінюваний в колах C ++ ІМА.

Якщо ви обробляєте колекцію предметів (дуже поширене forвикористання у циклі), то вам дійсно слід скористатися більш спеціалізованим методом. На жаль, std::for_eachце досить болісно в C ++ з ряду причин. У багатьох випадках відокремлення корпусу forпетлі у вільно стоячій функції (хоча і дещо болісно) призводить до набагато чистішого рішення. При роботі з колекціями, розглянемо std::for_each, std::transformчи std::accumulate. Реалізація багатьох алгоритмів стає стислою і кришталево зрозумілою, коли виражається таким чином. Не кажучи вже про те, що виділення корпусу циклу в окрему функцію / метод змушує концентруватися на алгоритмі, його вхідних вимогах та результатах.

Якщо ви використовуєте Java, Python, Ruby або навіть C ++ 0x , то вам слід використовувати відповідний цикл foreach колекції . Коли компілятори C ++ реалізують цю функцію, кількість forциклів зникне, як і ці типи дискусій.


2
"Однак використання менш обмежувального оператора є дуже поширеною ідіомою оборонного програмування." Це підсумовує його більш-менш.
Джеймс П.

+1 Для discussin відмінності в намірах з порівнянням з while, forі foreach(або мовою equivilant)
Кевін Пено

Строго кажучи, цикл використовується для продовження обробки до тих пір , поки стан конкретного буде НЕ зустрілоwhile
Альнитак

@Alnitak - D'oh ... Я це
виправлю

Мене бентежили два можливі значення "менш обмежуючого": це може означати, що оператор є поблажливішим у значеннях, які він передає ( <), або оператор поблажливіший у значеннях, які він не дає ( !=).
Mateen Ulhaq

4

Використовуючи "менше" - це (як правило) семантично правильно, ви дійсно маєте на увазі підрахунок, поки iне стане менше 10, тому "менше" чітко передає ваші наміри. Використання "не рівного" очевидно працює практично у випадках виклику, але передає дещо інше значення. У деяких випадках це може бути те, що вам потрібно, але, на мій досвід, цього ніколи не було. Якщо у вас дійсно був випадок, коли це iможе бути більше або менше 10, але ви хочете продовжувати циклічно, поки він не дорівнює 10, тоді цей код дійсно потребує коментування дуже чітко, і, ймовірно, може бути краще записати якусь іншу конструкцію, наприклад як whileпетля, можливо.


2

Одна з причин , чому я виступаю за less thanбільше not equals, щоб діяти в якості охоронця. У деяких обмежених обставинах (погане програмування чи санітарія) це not equalsможе бути пропущено, тоді як less thanвсе ще діє.

Ось один приклад, коли відсутність перевірки санітарії призвело до незвичайних результатів: http://www.michaeleisen.org/blog/?p=358


0

Ось одна з причин, чому ви хочете скористатися, <а не використовувати !=. Якщо ви використовуєте мову з глобальним масштабуванням змінної, що станеться, якщо зміниться інший код i? Якщо ви використовуєте, <а не !=найгірше, що трапляється, це те, що ітерація закінчується швидше: можливо, деякі інші збільшення коду iвипадково, і ви пропускаєте кілька ітерацій у циклі for. Але що станеться, якщо ви циклуєте від 0 до 10, а цикл потрапляє на 9, а iз незрозумілої причини збільшується неправильний приріст потоку . Тоді ваш цикл закінчує цю ітерацію та приріст, iтак що це значення тепер 11. Оскільки цикл пропустив умову виходу ( iніколи не дорівнював 10), тепер він буде циклічно нескінченний.

Це не обов'язково має бути особливо вигадливою логікою типу "прорізування" та "глобальної змінної", яка викликає це. Можливо, ви просто пишете цикл, який потребує зворотного відстеження. Якщо ви мутуєте iвсередині циклу, і ви накручуєте свою логіку, так як вона має верхню межу, а не a !=, менше шансів залишити вас у нескінченному циклі.


3
Звичайно, це здається ідеальним аргументом для кожного циклу і більш функціональним стилем програмування взагалі. Але чому б ви хотіли це робити, коли змінні змінні настільки веселіші ?!
Том Морріс

3
Я не думаю, що це страшенно вагомі причини. У будь-якому випадку ви отримаєте помилку, яку потрібно знайти та виправити, але нескінченна петля, як правило, робить помилку досить очевидною.
ak2

0

У вбудованому світі, особливо в галасливих умовах, ви не можете розраховувати, що оперативна пам'ять обов'язково поводиться як слід. У умовному (for, while, if), де ви порівнюєте, використовуючи '==' або '! =', Ви завжди ризикуєте, що ваші змінні пропустили важливе значення, яке припиняє цикл - це може мати катастрофічні наслідки - Mars Lander рівень наслідків. Використання "<" або ">" в умові забезпечує додатковий рівень безпеки для лову "невідомих невідомих".

У початковому прикладі, якби iнезрозуміло катапультувались із значенням, значно більшим за 10, порівняння '<' відразу вловлює помилку та виходить із циклу, але '! =' Продовжує рахувати, поки не iзавернеться минуле 0 та назад до 10.


2
Ще одна пов'язана варіація існує з кодом, наприклад, for (i=4; i<length; i++) csum+=dat[i];коли легальні пакети завжди мали lengthб принаймні чотири, але один може отримувати пошкоджені пакети. Якщо різні типи пакетів мають різні вимоги до довжини, можливо, ви хочете перевірити довжину як частину обробки, яка є різною для кожного типу пакету, але перевірити спочатку контрольну суму як частину загальної логіки. Якщо пакет низької довжини отриманий, це не має особливого значення, чи підрахунок контрольної суми повідомляє "добре" чи "погано", але він не повинен виходити з ладу.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.