Чи погані практики програмування `break 'та` continue`?


191

Мій начальник постійно згадує, що погані програмісти використовують breakі continueв циклі.

Я їх постійно використовую, бо вони мають сенс; дозвольте показати вам натхнення:

function verify(object) {
    if (object->value < 0) return false;
    if (object->value > object->max_value) return false;
    if (object->name == "") return false;
    ...
}

Суть у тому, що спочатку функція перевіряє правильність умов, а потім виконує фактичну функціональність. IMO те саме стосується циклів:

while (primary_condition) {
    if (loop_count > 1000) break;
    if (time_exect > 3600) break;
    if (this->data == "undefined") continue;
    if (this->skip == true) continue;
    ...
}

Я думаю, що це полегшує читання та налагодження; але я також не бачу недоліків.


2
Не потрібно багато, щоб забути, хто робить що.

57
Ні. Знання, коли ними користуватися, є ключовим. Вони є інструментами в панелі інструментів. Ви використовуєте їх, коли вони надають чіткий та короткий код.
orj

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

9
Очевидно, ваш начальник не пише (достатньо) коду. Якби він це зробив, він би знав, що всі ключові слова (так, навіть goto) корисні в деяких випадках.
sakisk

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

Відповіді:


240

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

При використанні в середині блоку, з деяким кодом навколо, вони діють як приховані пастки, тому це погано.


6
@Klaim: Можна стверджувати, що будь-яка програма, яка має кілька точок виходу, є погано обгрунтована. Правильно розроблений режим повинен робити одне і лише одне.
біт-твідлер

79
@ bit-twiddler: це дуже настрій C-ish. Тимчасову змінну можна згодом змінити, тож один типовий 20 рядків внизу звідси може стерти цей ретельно оброблений результат. Однак негайне повернення (або перерва, або продовження), проте, є надзвичайно зрозумілим: я можу припинити читання зараз, бо знаю, що його неможливо змінити далі . Це добре для мого маленького мозку, справді полегшує прокручування коду.
Матьє М.

15
@Matthieu Я згоден. Вийдіть з блоку, коли отримаєте результат, який задовольняє мету блоку.
Еван Плейс

8
@ bit-twiddler - принцип єдиної точки виходу є окремим від принципу єдиної відповідальності. Я не згоден з тим, що перевірка завжди є окремою від дії WRT - єдиної відповідальності. Насправді "єдина відповідальність" завжди вважає мене суб'єктивним терміном. Наприклад, у випадку рішення квадратику, чи слід розраховувати дискримінанта окрему відповідальність? Чи може вся квадратична формула бути єдиною відповідальністю? Я заперечую, що це залежить від того, чи є ви окремим способом використання дискримінанта - інакше трактування цього як окремої відповідальності, ймовірно, є надмірним.
Steve314

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

87

Ви можете прочитати документ Дональда Кнута 1974 р. " Структуроване програмування" із документом "Перейти до заяви" , в якому він обговорює різні способи використання, go toякі є структурно бажаними. Вони включають еквівалент breakта continueтвердження (багато застосувань go toу них були розроблені в більш обмежені конструкції). Ваш начальник тип називати Кнут поганим програмістом?

(Приклади наведені мене цікавлять. Як правило, breakі continueне люблять людей , які люблять один вхід і один вихід з будь-якої частини коду, і що така людина також хмуриться на кілька returnтверджень.)


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

5
Паскаль також мав вкладені функції. Просто кажу ...
Shog9

6
З того, що я пам’ятаю, ще в той час основною причиною того, що людям не подобалися декілька операторів повернення у функціях, було те, що налагоджувачі не працювали з ними належним чином. Бути справжньою болем було встановити точку перерви в кінці функції, але ти ніколи не потрапив у неї через попередню заяву про повернення. Для кожного компілятора я використовую сьогодні це вже не проблема.
Данк

28
@ bit-twiddler: Я не дуже впевнений у цьому. Я все ще використовую Паскаль сьогодні, і я, як правило, розглядаю "єдиний вхід з одним виїздом" або, принаймні, його частину з одним виїздом, як програмне культове культ. Я просто розглянути Break, Continueі в Exitякості інструменту в моїй панелі інструментів; Я використовую їх там, де це полегшує дотримання коду, і не використовую їх там, де це ускладнить читання.
Мейсон Уілер

2
@ bit-twiddler: амінь до цього. Я також додам, що коли ви переходите до блоків, які легко поміщаються на екрані, кілька точок виходу стають набагато менш клопіткими.
Shog9

44

Я не вірю, що вони погані. Ідея, що вони погані, походить із часів структурованого програмування. Це пов'язано з поняттям, що функція повинна мати одну точку входу та одну точку виходу, тобто лише одну returnна функцію.

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

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

Ці питання розглядаються у "Чистому кодексі" Роберта К. Мартіна та у "Рефакторингу" Мартіна Фаулера.


12
"Зробіть свої функції маленькими. Потім зробіть їх меншими" -Роберт Мартін. Я виявив, що це працює напрочуд добре. Кожен раз, коли ви бачите блок коду у функції, який потребує коментаря з поясненням того, що він робить, загортайте його в окрему функцію з описовою назвою. Навіть якщо це лише кілька рядків, і навіть якщо він використовується лише один раз. Ця практика усуває більшість проблем із перервою / продовженням або багаторазовим поверненням.
Діма

2
@Mikhail: Цикломатична складність, як правило, досить сильно корелює зі SLOC, що означає, що поради можуть бути спрощені, щоб "не писати довгих функцій".
Джон Р. Стром

3
Ідея єдиної точки виходу широко трактується неправильно. Колись функціям не довелося повертати абонента. Вони могли повернутися в якесь інше місце. Це зазвичай робилося мовою асемблера. Фортран мав для цього спеціальну конструкцію; Ви можете передати номер оператора, який передує ampersand CALL P(X, Y, &10), і в разі помилки, функція може передавати керування цьому оператору, а не повертатися до точки виклику.
Кевін Клайн

@kevincline, як, наприклад, з Lua.
Qix

1
@cjsimon ти це отримав. Не тільки функції повинні бути невеликими. Заняття теж повинні бути невеликими.
Діма

39

Погані програмісти говорять в абсолютах (так само, як і Сіт). Хороші програмісти використовують найяскравіше можливе рішення (при всіх інших рівнях ).

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

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


Так, я перебільшував кількість умов, щоб навести приклади випадків виходу.
Михайло

9
Який приклад запропонованого вами коду заміни? Я подумав, що це досить розумний приклад заяв охорони.
simgineer

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

@nocomprende Я не впевнений, що ти отримуєш. Тут "можливе" не означає, що найкращого рішення не існує - лише те, що ідеальна, остаточна ясність - це не річ. Зрештою, це суб'єктивно.
Матвій

22

Більшість людей вважають, що це погана ідея, оскільки поведінку нелегко передбачити. Якщо ви читаєте код і бачите, while(x < 1000){}що припускаєте, що він буде працювати до x> = 1000 ... Але якщо в середині є розриви, то це не відповідає дійсності, тому ви не можете довіряти своєму петля ...

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

Для себе, якби я збирався зробити цикл, який розірвався за більше ніж одна умова, я б while(x){}тоді переключив X на хибне, коли мені потрібно було вийти з ладу. Кінцевий результат був би таким же, і кожен, хто читає код, знав би уважніше придивитися до речей, які змінили значення X.


2
+1 дуже добре сказано, і +1 (якщо я можу зробити інше) за while(notDone){ }підхід.
FrustratedWithFormsDesigner

5
Михайло: Проблема з розривом полягає в тому, що остаточна умова циклу ніколи не проставляється в одному місці. Це ускладнює передбачення стану після циклу. У цьому тривіальному випадку (> = 1000) це не важко. Додайте багато if-заяв та різних рівнів вкладених файлів, визначити пост-стан циклу може дуже важко.
S.Lott

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

6
Заміна break;з x=false;робить ваш код зрозумілішим. Ви все ще повинні шукати тіло для цього твердження. І у випадку, x=false;якщо вам доведеться перевірити, чи не вдарив він x=true;далі.
Sjoerd

14
Коли люди кажуть: "Я бачу х, і я вважаю, що так, але якщо ви робите z, це припущення не відповідає" я схильний думати ", тому не робіть цього дурного припущення". Багато людей спростили б це до "коли я бачу, while (x < 1000)я припускаю, що він працюватиме 1000 разів". Ну, є багато причин, чому це неправда, навіть якщо xспочатку дорівнює нулю. Наприклад, хто каже x, що збільшується точно один раз під час циклу і ніколи не змінюється іншим способом? Навіть для вашого власного припущення, лише тому, що щось встановлюється, x >= 1000це не означає, що цикл закінчиться - він може бути встановлений в діапазоні, перш ніж умова перевіряється.
Steve314

14

Так, ви можете [повторно] писати програми без заяв про перерви (або повернення з середини циклів, які роблять те саме). Але, можливо, вам доведеться ввести додаткові змінні та / або дублювання коду, які, як правило, ускладнюють розуміння програми. Паскаль (мова програмування) був дуже поганим, особливо для початківців програмістів саме з цієї причини. Ваш начальник, по суті, хоче, щоб ви запрограмували в контрольних структурах Паскаля. Якби у Ваших черевиках був Лінус Торвальдс, він, мабуть, показав вашому начальнику середній палець!

Є результат інформатики, який називається ієрархією управлінських структур Косараджу, яка починається з 1973 року, і про яку згадується у відомому документі Кнута про готи з 1974 року. .) Що довів С. Рао Косараджу в 1973 році, це те, що неможливо переписати всі програми, які мають багаторівневі перерви глибини n, в програми з глибиною розриву менше n без введення зайвих змінних. Але скажімо, що це лише суто теоретичний результат. (Просто додайте кілька додаткових змінних ?! Безумовно, ви можете це зробити, щоб догодити своєму начальнику ...)

Що набагато важливіше з точки зору інженерії програмного забезпечення - це нещодавніший документ Еріка С. Робертса з 1995 р. Під назвою Вихід з циклу та структуроване програмування: Повторне відкриття дискусії ( http://cs.stanford.edu/people/eroberts/papers/SIGCSE- 1995 / LoopExits.pdf ). Робертс підсумовує кілька емпіричних досліджень, проведених іншими перед ним. Наприклад, коли групі студентів типу CS101 було запропоновано написати код функції, що реалізує послідовний пошук у масиві, автор дослідження сказав наступне про тих студентів, які використовували перерву / повернення / goto для виходу з послідовний цикл пошуку, коли елемент був знайдений:

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

Робертс також говорить, що:

Студенти, які намагалися вирішити проблему, не використовуючи явного повернення з циклу for for, пройшли набагато менше: лише семеро з 42 студентів, які намагалися використовувати цю стратегію, змогли створити правильні рішення. Цей показник представляє рівень успішності менше 20%.

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

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


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

9

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


:) так, приклад, який я надавав, був концептуальним
Михайло

7

Приклад, який ви подали, не потребує перерв і не продовжує:

while (primary-condition AND
       loop-count <= 1000 AND
       time-exec <= 3600) {
   when (data != "undefined" AND
           NOT skip)
      do-something-useful;
   }

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

У моєму вкладеному підході, чим глибше ви проходите, тим «кориснішим» стає код.

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


+1 Але насправді ваші <порівняння повинні <=відповідати рішенню ОП
El Ronnoco

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

6

"Поганість" залежить від того, як ви їх використовуєте. Я зазвичай використовую перерви в циклічних конструкціях ТІЛЬКИ, коли це врятує мені цикли, які неможливо зберегти за допомогою рефакторингу алгоритму. Наприклад, проїзд по колекції шукає предмет зі значенням у певному властивості, встановленому на true. Якщо все, що вам потрібно знати, - це те, що для одного з елементів було встановлено значення властивості true, як тільки ви досягнете цього результату, перерву добре перервати циклом належним чином.

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


Я згоден з обома пунктами! Щодо вашого другого пункту - я думаю, що простіше слідувати моїм початковим повідомленням, оскільки він читається як англійська. Якщо у вас є комбінація умов у викладі 1 if, то це майже розшифровка, щоб з'ясувати, що має відбутися для if, щоб виконати true.
Михайло

@Mikhail: Надані вами зразки трохи альтруїстичні для конкретного реалізму. Як я бачу, ці зразки чіткі, стислі, їх легше читати. Більшість петель не такі. Більшість циклів мають якусь іншу логіку, яку вони виконують, і, можливо, набагато складніші умови. Це в тих випадках, коли перерва / продовження може бути не найкращим використанням, оскільки це замулює логіку при читанні.
Джоель Етертон

4

Абсолютно ні ... Так використання gotoпогано, оскільки воно погіршує структуру вашої програми, а також дуже важко зрозуміти потік управління.

Але використання висловлювань як breakі continueє абсолютно необхідними в наші дні і зовсім не вважається поганою практикою програмування.

А також не так складно зрозуміти контрольний потік при використанні breakі continue. У конструкціях , як switchв breakзаяві абсолютно необхідно.


2
Я не використовував "продовжити", оскільки я вперше вивчив C в 1981 році. Це непотрібна мовна функція, оскільки код, який обійдений оператором "продовження", може бути обгорнутий умовним оператором керування.
біт-twiddler

12
Я вважаю за краще використовувати продовжувати в тих випадках, оскільки це гарантує, що мій код не стане кодом стрілки. Я ненавиджу код стрілки більше, ніж заяви goto type. Я також читаю це як "якщо це твердження відповідає дійсності, пропустіть решту цього циклу і продовжуйте наступну ітерацію". Дуже корисно, коли він знаходиться на самому початку циклу for (менш корисний у циклі while).
jsternberg

@jsternberg За виграш! :-)
Notinlist

3

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

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

Чесно кажучи, ваш другий код не очевидний. Що це робить? Чи продовжується "продовження", чи це "наступний цикл"? Я поняття не маю. Принаймні ваш перший приклад зрозумілий.


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

ваш "Відверто" коментар є синтаксичним - "наступний" - це суттєва річ; звичайні мови використовують "продовжувати", щоб означати "пропустити цю петлю"
Михайло

@Mik, можливо, "пропустити цю ітерацію" було б кращим описом. З повагою, доктор І. М. Педантік
Піт Вілсон

@Mikhail: Звичайно. Але коли людина працює на багатьох мовах, це може призвести до помилок, коли синтаксис не є загальним між мовами.
Пол Натан

Але математика - це не програмування. Код виразніше, ніж математика. Я розумію, що блок-схеми однократного входу / одного виходу можуть зробити схеми поліпшеними, але за рахунок яких коштів (тобто, коли перерва / повернення може зробити код ефективнішим)?
Еван Плейс

3

Я б замінив ваш другий фрагмент коду

while (primary_condition && (loop_count <= 1000 && time_exect <= 3600)) {
    if (this->data != "undefined" && this->skip != true) {
        ..
    }
}

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


Я не погоджуюся з тим, що "я насправді думаю, що це легше читати", але це відповідає точно такій самій цілі, що і код вище. Я ніколи не думав про «розрив» як оператора короткого замикання до цих пір, але це має ідеальний сенс. Що стосується "Загалом кажучи, умови для ваших циклів повинні міститися виключно в цих умовах циклу", що ви робите, обробляючи цикл foreach, оскільки насправді немає можливості вставити умовну логіку у визначення циклу?
Еван Плейс

@Evan Умова не застосовується до foreachциклу, оскільки це просто повторить кожен елемент колекції. forЦикл аналогічний тим , що він не повинен мати умовну кінцеву точку. Якщо вам потрібна умовна кінцева точка, тоді вам потрібно використовувати whileцикл.
El Ronnoco

1
@ Еван, я бачу вашу думку - тобто "що, якщо вам потрібно вирватися з цикла передбачення?" - Ну, breakз моєї точки зору має бути лише один максимум.
El Ronnoco

2

Я не згоден з вашим начальником. Є належні місця для використання breakта continueїх використання. Насправді причина того, що до сучасних мов програмування були введені винятки та поводження з винятками, є те, що не можна вирішити кожну проблему просто structured techniques.

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

while (primary_condition) {
    if (loop_count > 1000) || (time_exect > 3600) {
        break;
    } else if ( ( this->data != "undefined") && ( !this->skip ) ) {
       ... // where the real work of the loop happens
    }
}

З іншого боку примітка

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


Я роблю це питання довгі роки. Ваш шлях ('if (flag) {...}') набагато коротший і, можливо, він виглядає більш "професійним" чи "експертним". Але, знаєте, це часто означає, що читачеві / обслуговуючому персонажу доводиться коротко перебивати себе, щоб згадати, що означає конструкція; і щоб бути впевненим у сенсі тесту. Наразі я використовую 'if (flag == true) {...}' лише тому, що це здається кращою документацією. Наступного місяця? Quien sabe?
Піт Вілсон

2
@Pete - Термін не короткий, але елегантний . Ви можете вафельно робити все, що завгодно, але якщо ви переживаєте, що читач / супровідник не розуміє, що це booleanтаке, або що означає термінова / елегантна термінологія, то, можливо, вам краще найняти якісь розумніші супровідники ;-)
Zeke Hansell

@Pete, я також стою біля своєї заяви про згенерований код. Ви робите ще одне порівняння, порівнюючи прапор із постійною величиною перед оцінкою булевого значення виразу. Чому це зробити складніше, ніж це має бути, змінна прапор вже має потрібне вам значення!
Zeke Hansell

+1 хороша робота. Однозначно набагато елегантніше, ніж колишній приклад.
Еван Плейс

2

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

Для "розриву" витягніть саму петлю в окремий метод, замінивши "перерву" на "повернення".

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


0

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


2
Насправді, легко зрозуміти, чи правильно відступаєш, чи інтерналізується семантика цих висловлювань і чи не надто сонлива.

@delnan - це багато припущень;)
davidhaskins

2
Так, особливо останній.
Майкл К

Що ж, №1 потрібен для серйозного програмування, так чи інакше, варто чекати від усіх, кого називають програмістом, а №3 загалом корисний;)

Деякі мови (лише сценарії, які я знаю) підтримують break 2; для інших, я думаю, використовуються тимчасові прапори bool
Михайло

0

Поки вони не використовуються як замасковані готи, як у наступному прикладі:

do
{
      if (foo)
      {
             /*** code ***/
             break;
      }

      if (bar)
      {
             /*** code ***/
             break;
      }
} while (0);

Я з ними добре. (Приклад видно у виробничому коді, мех)


Чи рекомендуєте ви для таких випадків створювати функції?
Михайло

Так, а якщо не можливо, звичайний гото. Коли я бачу робити, я думаю, що повторення, а не контекст для імітації стрибка.
SuperBloup

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

0

Мені не подобається жоден із цих стилів. Ось що я віддаю перевагу:

function verify(object)
{
    if not (object->value < 0) 
       and not(object->value > object->max_value)
       and not(object->name == "") 
       {
         do somethign important
       }
    else return false; //probably not necessary since this function doesn't even seem to be defined to return anything...?
}

Мені дуже не подобається використовувати returnфункцію переривання функції. Це відчувається як зловживання return.

Використання breakтакож не завжди зрозуміло для читання.

Ще краще може бути:

notdone := primarycondition    
while (notDone)
{
    if (loop_count > 1000) or (time_exect > 3600)
    {
       notDone := false; 
    }
    else
    { 
        skipCurrentIteration := (this->data == "undefined") or (this->skip == true) 

        if not skipCurrentIteration
        {
           do something
        } 
        ...
    }
}

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

(Весь наведений вище код є псевдокодом)


11
Ви б дійсно вважали за краще 3 рівня гніздування над тим, що я набрав вище?
Михайло

@Mikhail: Так, або я призначив результат умови змінній. Мені це набагато легше зрозуміти, ніж логіку breakта continue. Аномально закінчується цикл просто дивно, і мені це не подобається.
FrustratedWithFormsDesigner

1
За іронією долі, ви неправильно прочитали мої умови. continueозначає пропустити функціональність перейти до наступного циклу; не "продовжувати страту"
Михайло

@Mikhail: Ага. Я не використовую її часто, і коли я читаю, я плутаюся в сенсі. Ще одна причина, що мені це не подобається. : P Дайте мені хвилину на оновлення ...
FrustratedWithFormsDesigner

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

0

Ні. Це спосіб вирішити проблему. Є й інші способи її вирішення.

Багато сучасних основних мов (Java, .NET (C # + VB), PHP, пишіть власні) використовують "break" і "продовжити" для пропуску циклів. Вони обидва речення "структуровані гот (и)".

Без них:

String myKey = "mars";

int i = 0; bool found = false;
while ((i < MyList.Count) && (not found)) {
  found = (MyList[i].key == myKey);
  i++;   
}
if (found)
  ShowMessage("Key is " + i.toString());
else
  ShowMessage("Not found.");

З ними:

String myKey = "mars";

for (i = 0; i < MyList.Count; i++) {
  if (MyList[i].key == myKey)
    break;
}
ShowMessage("Key is " + i.toString());

Зауважте, що "перерва" та "продовження" код коротший і зазвичай перетворюється "в той час як" речення на "за" або "передбачати" пропозиції.

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

Я фактично працюю в деяких проектах, де обов'язково було використовувати ці речення.

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

Висновок, навіть якщо я вважаю за краще не використовувати їх, я вважаю, що це варіант, а не погана практика програмування.


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

2
не кажучи вже про те, що перший приклад працює лише в тому випадку, якщо ключ є останнім у списку.
Михайло

@FrustratedWithFormsDesigner ТОЧНО. Я
надягаю на purpouse,

однак у вас є дві процедури з різною семантикою; отже, вони логічно не рівноцінні.
біт-твідлер

2
у вашому другому прикладі є дві помилки, одна синтаксична та одна логічна. 1. Він не буде компілюватися, оскільки ітератор не оголошується за межами циклу for (тому недоступний у рядковому виведенні). 2. Навіть якщо ітератор був оголошений за межами циклу, якщо ключ не знайдений у колекції, рядок виводить ключ останнього елемента у списку.
Еван Плейс

0

Я не проти continueі breakв принципі, але думаю, що це дуже низькі конструкції, які дуже часто можуть бути замінені чимось ще кращим.

Я використовую C # як приклад, розглянемо випадок, коли хочеться перебрати колекцію, але ми хочемо лише елементів, які виконують деякий предикат, і ми не хочемо робити більше, ніж максимум 100 ітерацій.

for (var i = 0; i < collection.Count; i++)
{
    if (!Predicate(item)) continue;
    if (i >= 100) break; // at first I used a > here which is a bug. another good thing about the more declarative style!

    DoStuff(item);
}

Це виглядає РОЗУМНО чисто. Це не дуже важко зрозуміти. Я думаю, що було б багато чого отримати від того, щоб бути більш декларативним. Порівняйте його з наступним:

foreach (var item in collection.Where(Predicate).Take(100))
    DoStuff(item);

Можливо, дзвінки «Де та приймати» навіть у цьому методі не повинні бути. Можливо, цю фільтрацію слід зробити перед тим, як колекція передається цьому методу. У будь-якому випадку, відходячи від речей низького рівня та зосереджуючись більше на власне логіці бізнесу, стає зрозуміліше, що нас актуально цікавить. Стає простіше розділити наш код на згуртовані модулі, які більше дотримуються передового дизайнерського практику тощо. на.

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


-1

Code Complete має хороший розділ про використання gotoта кілька повернень з рутини або циклу.

Взагалі це не погана практика. breakабо continueточно скажіть, що буде далі. І я з цим згоден.

Стів МакКоннелл (автор Code Complete) використовує майже ті ж приклади, що і ви, щоб показати переваги використання різних gotoоператорів.

Однак надмірне використання break або continueможе призвести до складного та незрозумілого програмного забезпечення.

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