Чи слід перемістити умову перерви у поле стану, якщо можливо, всередині циклу for-loop? [зачинено]


48

Іноді мені потрібні петлі, для яких потрібен перерва:

for(int i=0;i<array.length;i++){
    //some other code
    if(condition){
        break;
    }
}

Я відчуваю себе незручно писати

if(condition){
    break;
}

тому що вона споживає 3 рядки коду. І я виявив, що цикл можна переписати так:

                                   ↓
for(int i=0;i<array.length && !condition;i++){
    //some other code
}

Отже, моє запитання полягає в тому, чи корисна практика переміщення умови в поле стану, щоб зменшити рядки кодів, якщо це можливо?


7
Залежить від того, що conditionсаме.
Бергі

4
Є причина, чому misra забороняє використовувати "break" і "продовжувати" в циклі. Дивіться також: цей stackoverflow.com/q/3922599/476681
BЈовіћ

35
Я думаю, що кількість рядків сама по собі не є справжнім питанням. Це можна було написати одним рядком. якщо (умова) перерва; На мою думку, питання читабельності. Мені особисто не подобається порушувати цикл, окрім використання умови циклу.
Джо

97
тому що вона споживає 3 рядки коду Дуже, дуже, дуже погана причина не подобатися будь-якому стилю. IMO "споживає [ряд] рядків коду" десь між невідповідним і хорошим. Намагання ввести занадто багато логіки в занадто мало рядків призводить до нечитабельного коду та важко знайти помилок.
Ендрю Генле

3
Можливо, непопулярна думка: for(int i=0;i<array.length && !condition;i++)відносно рідкість і може бути не поміченою кимсь, хто просто скинув код; це може бути корисним випадком використання для whileабо do whileциклу, які частіше мають декілька умов розриву у визначенні циклу.
Жуль

Відповіді:


154

Ці два приклади, які ви дали, не є функціонально еквівалентними. У оригіналі перевірка стану виконується після розділу "якийсь інший код", тоді як у модифікованій версії це робиться спочатку, на початку корпусу циклу.

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


5
Я розумію ваші настрої, але я підтримував достатньо застарілого коду, коли цикл довгий кілька сотень рядків і є кілька умовних перерв. Це робить налагодження дуже важким, коли ви очікуєте, що код дістанеться до умовного перерви, який ви очікуєте, але попередній код застосований, це спочатку умовний перерву. Як ви обробляєте цей код? У коротких прикладах, як описано вище, легко забути про такий загальноприйнятий сценарій.
Берін Лорич

84
@BerinLoritsch: Правильний спосіб вирішити це - не мати циклів, довжиною кілька сотень рядків!
Кріс

27
@Walfrat: Якщо переписати це не варіант, то насправді вже не виникає питання. Сказавши, що зазвичай можна досить безпечно використовувати рефактор типу «Метод витягування» для переміщення блоків коду, навколо яких слід зробити коротший цикл основного методу і, сподіваємось, зробити логічний потік більш очевидним. Це справді справа в кожному випадку. Я буду стояти за моє загальне правило зберігання методів коротких, і, безумовно, зберігати логічну циклічну схему короткої, але я не хочу намагатися сказати нічого іншого, ніж загальне, хоча ...
Кріс

4
Стислість @AndrewHenle - це читабельність, принаймні в тому сенсі, що збільшення багатослівності завжди знижує читабельність та ремонтопридатність. Зниження LOC досягається за рахунок збільшення щільності та підвищення рівня абстракції і дуже тісно корелює з технічним обслуговуванням, як це засвідчено в інших пар Q / A на цьому самому сайті.
Левшенко

5
@Joe конструкція Do-while настільки рідкісна, що вона схильна до відключення розробників, яким потрібно згодом підтримувати цей код. Кілька чортів ніколи насправді їх не бачили! Я не впевнений, якщо час роботи покращить читабельність взагалі - якщо що-небудь, це збільшить труднощі для розуміння коду. Як приклад - моїй теперішній кодовій базі близько 15 років і близько 2 мільйонів LOC. Близько півсотні розробників вже торкнулися його. У ній рівно нульові петлі для виконання!
Т. Сар - Відновлення Моніки

51

Я не купую аргумент про те, що "він споживає 3 рядки коду", і, отже, це погано. Зрештою, ви можете просто написати це як:

if (condition) break;

і просто споживайте один рядок.

Якщо ifз'являється на півдорозі через цикл, це, звичайно, має існувати як окремий тест. Однак якщо цей тест з’являється в кінці блоку, ви створили цикл, який має тест продовження на обох кінцях циклу, можливо, додаючи складності коду. Будьте в курсі, що іноді if (condition)тест може мати сенс лише після виконання блоку.

Але якщо припустити, що це не так, застосувавши інший підхід:

for(int i=0;i<array.length && !condition;i++){
    //some other code
}

умови виходу зберігаються разом, що може спростити речі (особливо якщо " якийсь інший код " довгий).

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


14
@ jpmc26, краще? Ні. Дійсний альтернативний стиль? Звичайно.
Девід Арно

6
Краще, як важче зіпсувати, коли код буде відредаговано пізніше, і не страждати від будь-яких помітних недоліків.
jpmc26

4
Зберігати їх разом через "вміст циклу може бути довгим", здається, слабким аргументом. Коли ваш контент важко читати, у вас виникають інші проблеми, ніж збереження двох рядків коду.
BlueWizard

12
@ jpmc26 Не погоджуюся. Брекети - це зайве захаращення в найкоротших, однолінійних умовах відповідно до цієї відповіді. Без витонченішого і легшого на око. Вони як термінальний умовний оператор. Я ніколи не забував додати дужки, розбиваючи їх на більші блоки заяв; Я вже додаю нові рядки.
benxyzzy

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

49

Таке питання викликало дискусії майже настільки ж довго, як і програмування. Щоб кинути капелюх на ринг, я б пішов на версію з breakумовою, і ось чому (для цього конкретного випадку):

forПетля просто є вказівка перебору значень в циклі. Кодування умови розриву всередині цього просто затуманює логіку ІМО.

Наявність окремого breakдає зрозуміти, що під час звичайної обробки значення такі, як зазначено в forциклі, і обробка повинна тривати, за винятком випадків, коли ця умова настає.

Як трохи тло, я почав кодувати a break, потім поставив умову в forцикл на роки (під керівництвом виняткового програміста), а потім повернувся до breakстилю, тому що він просто почувався набагато чистішим (і був переважаючий стиль у різні томи коду, які я споживав).

Це ще одна з тих святих воєн наприкінці дня - деякі кажуть, що ніколи не слід вириватися з циклу штучно, інакше це не справжня петля. Робіть усе, що вам здається, читабельним (як для себе, так і для інших). Якщо зменшення натискань на клавіші - це справді ваша справа, зайдіть на гольф на вихідні - пиво необов’язково.


3
Якщо умова має гарне ім’я, наприклад, "знайдено" (наприклад, ви щось шукаєте через масив), тоді це гарна ідея помістити його в голову циклу for.
user949300

1
Мені сказали, що forцикли використовуються тоді, коли відомо кількість ітерацій (наприклад, коли немає умови порушення, але закінчення масиву / списку) і whileколи немає. Це, мабуть, стосується whileпетлі. Якщо читанність викликає занепокоєння, idx+=1всередині циклу читати набагато чистіше, ніж if/elseблок
Laiv,

Ви не можете просто поставити умову перерви у циклі, якщо за ним є код, тому, принаймні, вам доведеться написати "якщо (умова) {found = true; продовжити;}
gnasher729

Ми настільки звикли до for(int i=0; i<something;i++)циклів, що, мабуть, половина розробників насправді не пам’ятає, що forпетлі можуть використовуватися по-різному, і тому важко зрозуміти &&conditionдеталь.
Ральф Клеберхофф

@RalfKleberhoff Дійсно. Я бачив багатьох нових розробників, які висловлюють здивування, що вирази тристоронньої заяви не є обов'язковими. Я навіть не згадую востаннє, коли я бачив for(;;)...
Роббі Ді

43

forпетлі призначені для ітерації над чим-небудь 1 - вони не просто lol-давайте-тягнемо-який-небудь-випадковий-речі-з-в-body-and-put-them-in-the-loop-header - три вирази мають дуже конкретні значення:

  • Перший - для ініціалізації ітератора . Це може бути індекс, вказівник, об’єкт ітерації чи будь-що інше - доки він використовується для ітерації .
  • Друга - для перевірки, чи ми досягли кінця.
  • Третє - для просування в ітераторі.

Ідея полягає в тому, щоб відокремити обробку ітерації ( як її повторити) від логіки всередині циклу ( що робити в кожній ітерації). У багатьох мовах зазвичай є цикл для кожного, що позбавляє вас від деталей ітерації, але навіть якщо у вашій мові немає такої конструкції, або якщо вона не може бути використана у вашому сценарії, ви все одно обмежуєте заголовок циклу до обробки ітерації.

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

1 На відміну від інших циклів, які "чекають", щоб щось сталося. for/ foreachЦикл повинен мати поняття «список» елементи для ітерації по - навіть якщо цей список ледачий або нескінченний або реферату.


5
Це була моя перша думка, коли я читав питання. Розчарований, мені довелося прокрутити півдюжини відповідей, щоб побачити, хто це скаже
Немо,

4
Гм ... всі петлі призначені для ітерації - ось що означає слово "ітерація" :-). Я припускаю, що ви маєте на увазі щось на кшталт "повторення над колекцією" або "повторення за допомогою ітератора"?
psmears

3
@psmears Добре, можливо, я вибрав неправильне слово - англійська мова не є моєю рідною мовою, і коли я думаю про ітерацію, я думаю про "ітерацію над чимось". Я виправлю відповідь.
Ідан Ар'є

Проміжний випадок - це коли щось подібне someFunction(array[i]). Тепер обидві частини умови стосуються того, що ви повторюєте, і було б доцільно поєднувати їх із &&заголовком циклу.
Бармар

Я поважаю вашу позицію, але я не згоден. Я думаю, що forпетлі в основі - це лічильники, а не петлі над списком, і це підтримується синтаксисом forпопередніх мов (ці мови часто мали for i = 1 to 10 step 2синтаксис і stepкоманду - навіть дозволяючи початкове значення, яке не є індексом першого елемент - натякає на числовий контекст, а не на контекст списку). Тільки випадок використання , що я думаю , що опори forпетлі , як тільки ітерація список є parallelising, і це вимагає явного синтаксису зараз в будь-якому випадку для того , щоб не порушувати код робить, наприклад, свого роду.
Логан Пікап

8

Я рекомендую використовувати версію з if (condition). Це легше читати і простіше налагоджувати. Написання 3 додаткових рядків коду не зламає ваші пальці, але спростить наступну людину зрозуміти ваш код.


1
Тільки якщо (як це було прокоментовано) у блоці циклу немає сотень рядків коду, а логіка всередині не є ракетною наукою. Я думаю, що ваш аргумент в порядку, але я пропускаю щось на зразок naming thingsналежного, щоб зрозуміти, що відбувається і коли виконується умова порушення.
Laiv

2
@Laiv Якщо в циклі блоку є сотні рядків коду, то це більше проблем, ніж питання, де написати умову перерви.
Олександр

Ось чому я запропонував контекстуалізувати відповідь, оскільки це не завжди відповідає дійсності.
Laiv

8

Якщо ви повторюєте колекцію, де слід пропустити певні елементи, рекомендую новий варіант:

  • Фільтруйте свою колекцію перед ітерацією

І Java, і C # роблять це порівняно тривіально. Я не був би здивований, якби C ++ мав можливість зробити ітератор фантазії, щоб пропустити певні елементи, які не застосовуються. Але це дійсно лише варіант, якщо ваша обрана мова підтримує її, і причина, яку ви використовуєте, breakполягає в тому, що існують умови щодо того, обробляєте ви елемент чи ні.

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

  • Легше зрозуміти, коли цикл закінчується рано

Я працював над кодом, де ваш цикл for займає кілька сотень рядків з кількома умовними умовами та деякою складною математикою. Проблеми, з якими ви стикаєтеся з цим:

  • break команди, поховані в логіці, важко знайти і іноді дивно
  • Налагодження вимагає перейти через кожну ітерацію циклу, щоб по-справжньому зрозуміти, що відбувається
  • Багато кодів не має легко реверсивних рефакторингу або одиничних тестів, щоб допомогти з функціональною еквівалентністю після перезапису

У цій ситуації я рекомендую наступні вказівки в порядку уподобання:

  • Фільтруйте колекцію, щоб усунути необхідність, breakякщо це можливо
  • Складіть умовну частину умов для циклу
  • Помістіть умовні breakелементи у верхній частині петлі, якщо це можливо
  • Додайте багаторядковий коментар, привертаючи увагу до перерви, і чому вона існує

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


1
Більшість цієї відповіді - це добре. Але ... я бачу, як фільтрування колекції могло б усунути потребу в continueзаявах, але я не бачу, як вони сильно допомагають break.
user949300

1
@ user949300 takeWhileФільтр може замінити breakзаяви. Якщо у вас є такий - Java, наприклад, отримує їх лише на Jave 9 (не те, що самостійно це важко реалізувати)
Idan Arye

1
Але на Java ви все ще можете фільтрувати перед ітерацією. Використання потоків (1.8 або новіших версій) або використання колекцій Apache (1.7 або новіших версій).
Laiv

@IdanArye - існують додаткові бібліотеки, які додають такі засоби до API потоків Java (наприклад, JOO-lambda )
Jules

@IdanArye жодного разу не чув про те, щоб зайняти час, перш ніж ваш коментар. Явне пов'язування фільтра з замовленням вважає мене незвичним і хитким, оскільки в «нормальному» фільтрі кожен елемент отримує повернення правдивим або хибним, незалежно від того, що відбувається до або після. Таким чином ви можете запускати речі паралельно.
user949300

8

Я настійно рекомендую не менше здивуватися, якщо ви не отримаєте суттєвої вигоди в іншому випадку.

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

Тож ймовірно, що випадковий розробник припустить, що це лише стандарт для циклу і навіть не дивитися на це:

for(int i=0;i<array.length&&!condition;i++)

Якщо ви хочете використовувати цей стиль , незалежно, я рекомендую зміна частин for(int i=0;i<і ;i++)які говорять мозку читача , що це стандартне цикл.


Ще одна причина if- breakце те, що ви не завжди можете використовувати свій підхід із forумовою. Ви повинні використовувати if- breakколи умова перерви є надто складною, щоб сховатися в forоператорі або покладається на змінні, доступні лише всередині forциклу.

for(int i=0;i<array.length&&!((someWidth.X==17 && y < someWidth.x/2) || (y == someWidth.x/2 && someWidth.x == 18);i++)

Тож якщо ви вирішите піти з собою if- breakвас охоплюють. Але якщо ви вирішили піти з forумовами, вам доведеться використовувати суміш умов if- breakі - for. Для того щоб бути послідовними, вам доведеться переміщувати умови вперед-назад між умовамиfor та if- breakколи умови змінюються.


4

Ваша трансформація передбачає, що все condition, що оцінюється, trueяк ви входите в цикл. Ми не можемо коментувати правильність цього в цьому випадку, оскільки він не визначений.

Досягнувши успіху в "зменшенні рядків коду" тут, ви можете продовжити його перегляд

for(int i=0;i<array.length;i++){
    // some other code
    if(condition){
        break;
    }
    // further code
}

і застосувати те саме перетворення, доходячи до

for(int i=0;i<array.length && !condition;i++){
    // some other code
    // further code
}

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

Набагато безпечніша схема трансформації полягає у вилученні some other codeта оцінці conditionокремої функції

bool evaluate_condition(int i) // This is an horrible name, but I have no context to improve it
{
     // some other code
     return condition;
}

for(int i=0;i<array.length && !evaluate_condition(i);i++){
    // further code
}

4

TL; DR Робіть все, що читається найкраще у ваших конкретних обставинах

Візьмемо цей приклад:

for ( int i = 1; i < array.length; i++ ) 
{
    if(something) break;
    // perform operations
}

У цьому випадку ми не хочемо, щоб якийсь код у циклі for виконувався, якщо somethingце правда, тому переміщення тесту somethingв поле умови є розумним.

for ( int i = 1; i < array.length && !something; i++ )

Потім знову, залежно від того, чи somethingможна встановити trueперед циклом, але не всередині нього, це може запропонувати більше ясності:

if(!something)
{
    for ( int i = 1; i < array.length; i++ )
    {...}
}

А тепер уявіть це:

for ( int i = 1; i < array.length; i++ ) 
{
    // perform operations
    if(something) break;
    // perform more operations
}

Ми надсилаємо туди дуже чітке повідомлення. Якщо somethingпід час обробки масиву стає істинним, тоді відмовтеся від усієї операції. Для того, щоб перенести чек у поле стану, вам потрібно виконати:

for ( int i = 1; i < array.length && !something; i++ ) 
{
    // perform operations
    if(!something)
    {
        // perform more operations
    }
}

Можливо, "повідомлення" затуманилося, і ми перевіряємо стан відмови в двох місцях - що робити, якщо умова відмови зміниться, і ми забудемо одне з цих місць?

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

Запишіть код так, щоб він добре читався (це, очевидно, дещо суб’єктивно) та стисло. Поза драконівським набором правил кодування всі "правила" мають винятки. Напишіть те, що вам здається, виражає рішення, яке найкраще приймає рішення, і мінімізуйте шанси на майбутні помилки.


2

Хоча це може бути привабливим, оскільки ви бачите всі умови у заголовку циклу і breakможуть бути заплутаними у великих блоках, forцикл - це шаблон, де більшість програмістів очікують циклічного перегляду певного діапазону.

Тим більше, що ви це зробите, додавання додаткової умови перерви може викликати плутанину, і важче зрозуміти, чи є вона функціонально еквівалентною.

Часто хорошою альтернативою є використання whileабо do {} whileциклу, який перевіряє обидві умови, припускаючи, що ваш масив має принаймні один елемент.

int i=0
do {
    // some other code
    i++; 
} while(i < array.length && condition);

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


Незважаючи на це питання , має деякі хороші відповіді вже, я хотів би спеціально upvote це тому , що він робить важливий момент: мені, маючи що - або інше , ніж проста перевірка кордонів в для циклу ( for(...; i <= n; ...)) на самому ділі робить код важче читати , тому що у мене є зупинитися і подумати про те, що відбувається тут, і, можливо, пропустить стан взагалі при першому проході, тому що я не очікую, що він там буде. Для мене forцикл означає, що ви перебуваєте в циклі через деякий діапазон, якщо є спеціальна умова виходу, його слід зробити явним за допомогою if, або ви можете використовувати while.
CompuChip

1

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

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

array.find(item -> item.valid)

Це повинно помітно скоротити рішення, одночасно спростивши його, оскільки ваш код конкретно говорить, що вам потрібно.


1

Мало причин, на мою думку, що говорить, не слід.

  1. Це знижує читабельність.
  2. Вихід може бути не однаковим. Однак це можна зробити за допомогою do...whileциклу (не while), оскільки умова перевіряється після виконання деякого коду.

Але поверх цього, врахуйте це,

for(int i=0;i<array.length;i++){

    // Some other code
    if(condition1){
        break;
    }

    // Some more code
    if(condition1){
        break;
    }

    // Some even more code
}

Тепер ви можете реально досягти цього, додавши ці умови до цього forстану?


Що таке " відкриття "? Ви маєте на увазі " думку "?
Пітер Мортенсен

Що ви маєте на увазі під «Кількома причинами, які говорять, не слід» ? Чи можете ви докладно?
Пітер Мортенсен

0

Я думаю, що вилучення каністру breakможе бути гарною ідеєю, але не зберігати рядки коду.

Перевага написання без цього breakполягає в тому, що пост-стан циклу є очевидним і явним. Коли ви закінчите цикл і виконайте наступний рядок програми, ваша умова циклу i < array.length && !conditionповинна бути помилковою. Отже, або iбільша, або дорівнює довжині вашого масиву, або conditionце правда.

У версії с break, є прихована ґутка. Умова циклу говорить про те, що цикл завершується, коли ви запускаєте інший код для кожного дійсного індексу масиву, але насправді є breakтвердження, яке може закінчити цикл до цього, і ви його не побачите, якщо не переглянете код циклу дуже обережно. І автоматизовані статичні аналізатори, включаючи оптимізацію компіляторів, можуть також мати труднощі з визначенням того, що таке умови циклу.

Це було первісною причиною, по якій Едгар Дайкстра написав "Гото вважається шкідливим". Це не було питанням стилю: виривання з циклу ускладнює формальне обґрунтування стану програми. Я написав багато break;тверджень у своєму житті, і колись, дорослий, я навіть написав goto(щоб вирватися з декількох рівнів критичної продуктивності внутрішнього циклу, а потім продовжити іншу гілку дерева пошуку). Однак я набагато більше шансів написати умовне returnтвердження з циклу (який ніколи не запускає код після циклу і тому не може вимкнути його умови), ніж a break.

Постскрипт

Насправді, для рефакторингу коду для здійснення такої ж поведінки фактично може знадобитися більше рядків коду. Ваш рефакторинг може бути дійсним для певного іншого коду або, якщо ми можемо довести, що conditionце почне неправдиво. Але ваш приклад рівнозначний:

if ( array.length > 0 ) {
  // Some other code.
}
for ( int i = 1; i < array.length && !condition; i++ ) {
  // Some other code.
}

Якщо якийсь інший код не є однолінійним, ви можете перенести його у функцію, щоб не повторюватися, і тоді ви майже майже переробили цикл у рекурсивну функцію.

Я усвідомлюю, що це було не головним моментом у вашому запитанні, і ви все це просте.


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

Ось чому я не згоден: якщо ви знаєте, що немає breakтвердження, то ви знаєте, що таке цикл і що цикл зупиняється, коли ця умова є помилковою. Якщо є break? Тоді є кілька способів, коли цикл може закінчитися, і в загальному випадку, з'ясування того, коли один з них може зупинити цикл, буквально вирішує проблему зупинки. На практиці моє велике занепокоєння полягає в тому, що цикл виглядає так, як це робить одне, але насправді той, хто написав код після циклу, не звернув уваги на крайній випадок, похований у його складній логіці. Речі в стані петлі важко не помітити.
Девіслор

0

Так, але ваш синтаксис нетрадиційний і, отже, поганий (tm)

Сподіваємось, ваша мова підтримує щось подібне

while(condition) //condition being a condition which if true you want to continue looping
{
    do thing; //your conditionally executed code goes here
    i++; //you can use a counter if you want to reference array items in order of index
}

2
можливо, я мав би використати інше слово «умова», я не мав на увазі це буквально означати саме таку ж умову в прикладі
Еван

1
Мало того, що цикл for не є навіть віддалено нетрадиційним, немає абсолютно жодної причини, коли цикл цієї форми є кращим, ніж еквівалент циклу. Насправді більшість людей сказали б, що це гірше, наприклад автори Основних рекомендацій CPP
Шон Бертон,

2
Деякі люди просто ненавидять творчі альтернативи. Або дивитися на проблему по-іншому, ніж той, який встановлюється в їхніх головах. Або взагалі розумні дупи. Я відчуваю твій біль брато!
Мартін Мейт

1
@sean Вибач чувак, я знаю, що люди ненавидять говорити, що вони помиляються, але я посилаюсь на es.77
Еван

2
Це whileпідкреслює монстра в коді - виняток, який інакше прихований у рутинному / мирському коді. Бізнес-логіка передня і центральна, петляна механіка занулена. Це дозволяє уникнути реакції "зачекайте хвилину ..." на forцикл. Ця відповідь вирішує проблему. абсолютно до голосування.
radarbob

0

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

(Особисто мені не подобається правило, що у ifвисловлюваннях завжди повинні бути дужки, що значною мірою є причиною марно витраченого вертикального простору. Зверніть увагу, що проблема витрачає вертикальний простір. Використання вертикального простору зі значущими лініями не повинно бути проблемою.)

Один варіант - розділити його на кілька ліній. Це, безумовно, що ви повинні зробити, якщо ви використовуєте дійсно складні forоператори, з кількома змінними та умовами. (Це для мене краще використовувати вертикальний простір, надаючи якомога більше рядків щось значуще.)

for (
   int i = 0, j = 0;
   i < array.length && !condition;
   i++, j++
) {
   // stuff
}

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

array
.takeWhile(condition)  // condition can be item => bool or (item, index) => bool
.forEach(doSomething); // doSomething can be item => void or (item, index) => void

Розбиття складного чи незвичайного forтвердження на кілька рядків - найкраща ідея у всій цій дискусії
user949300

0

Забули одне: коли я перериваюся з циклу (не виконую всі можливі ітерації, незалежно від того, як реалізовано), зазвичай так буває, що я не лише хочу вийти з циклу, але й хочу знати, чому це сталося . Наприклад, якщо я шукаю предмет X, я не тільки відриваюсь від циклу, але й хочу знати, що X знайдено і де він знаходиться.

У цій ситуації мати стан поза циклом цілком природно. Так я починаю з

bool found = false;
size_t foundIndex;

і в циклі я напишу

if (condition) {
    found = true;
    foundIndex = i;
    break;
}

з петлею for

for (i = 0; i < something && ! found; ++i)

Зараз перевірка стану в циклі цілком природна. Від того, чи є заява "break", залежить від того, чи є подальша обробка в циклі, якого ви хочете уникнути. Якщо потрібна деяка обробка, а інша - ні, ви можете мати щось подібне

if (! found) { ... }

десь у вашій петлі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.