Являются ли `break` и` continue` методами программирования?


192

Мой босс продолжает упоминать беспечно , что плохие программисты используют breakи continueв петлях.

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

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

Дело в том, что сначала функция проверяет правильность условий, а затем выполняет фактическую функциональность. ИМО же относится и к петлям:

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) полезны в некоторых случаях.
Сакиск

57
Плохие программисты используют break и continue не означает, что хорошие программисты этого не делают. Плохие программисты используют, если и в то же время.
Mouviciel

Ответы:


241

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

При использовании в середине блока с некоторым кодом они действуют как скрытые ловушки, так что это плохо.


6
@Klaim: Можно утверждать, что любая процедура, которая имеет несколько точек выхода, является плохо продуманной. Правильно продуманная рутина должна делать одно и только одно.
bit-twiddler

79
@ bit-twiddler: это очень странное мышление. Временная переменная может быть изменена позже, поэтому одна опечатка на 20 строк отсюда может стереть этот тщательно обработанный результат. Немедленное возвращение (или прерывание, или продолжение), однако, предельно ясно: я могу прекратить чтение сейчас, потому что я знаю, что его нельзя изменить дальше . Это хорошо для моего маленького мозга, действительно облегчает прохождение кода.
Матье М.

15
@Matthieu Я согласен. Выйдите из блока, когда вы получите результат, который удовлетворяет цели блока.
Эван Плейс

8
@ bit-twiddler - принцип единой точки выхода отделен от принципа единой ответственности. Я не согласен с тем, что проверка всегда отделена от действия WRT единоличной ответственности. На самом деле «единая ответственность» всегда кажется мне субъективным термином. Например, в квадрато-решателе, должен ли расчет дискриминанта быть отдельной обязанностью? Или вся квадратичная формула может быть единственной ответственностью? Я бы сказал, что это зависит от того, используете ли вы отдельное использование дискриминанта - в противном случае, рассмотрение его как отдельной ответственности, вероятно, является чрезмерным.
Steve314

10
Этот ответ - практическое правило, а не жесткое правило. В большинстве случаев это работает, не стесняйтесь нарушать его, если это имеет смысл в вашем контексте.
Klaim

87

Вы можете прочитать статью Дональда Кнута « Структурированное программирование » 1974 года , в которой можно перейти к «Утверждениям» , в которой он обсуждает различные варианты использования, go toкоторые являются структурно желательными. Они включают эквивалент breakи continueоператоры (многие из применений go toв них были разработаны в более ограниченные конструкции). Ваш начальник - тип, который называет Кнута плохим программистом?

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


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

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

6
Из того, что я помню, в то время главная причина того, что людям не нравились множественные операторы return в функциях, заключалась в том, что отладчики не обрабатывали их должным образом. Было очень трудно установить точку останова в конце функции, но вы никогда не достигли ее из-за более раннего оператора return. Для каждого компилятора, который я сейчас использую, это больше не проблема.
Данк

28
@ bit-twiddler: я не уверен в этом. Я до сих пор использую Pascal и обычно рассматриваю «единый выход с одним входом» или, по крайней мере, часть с одним выходом, как программирование культа грузов. Я просто считаю Break, Continueи Exitкак инструменты в моем наборе инструментов; Я использую их там, где это облегчает понимание кода, и не использую их там, где это усложнит чтение.
Мейсон Уилер

2
@ Bit-Twiddler: аминь к этому. Я также добавлю, что, как только вы перейдете к блокам, которые легко помещаются на экране, несколько точек выхода станут гораздо менее хлопотными.
Shog9

44

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

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

Если ваша функция очень короткая, если у вас один цикл или, в худшем случае, два вложенных цикла, и если тело цикла очень короткое, тогда очень ясно, что делает a breakили a continue. Также ясно, что returnделают несколько утверждений.

Эти проблемы рассматриваются в «Чистом коде» Роберта К. Мартина и в «Рефакторинге» Мартина Фаулера.


12
«Сделай свои функции маленькими. Затем сделай их меньше», - Роберт С. Мартин. Я обнаружил, что это работает на удивление хорошо. Каждый раз, когда вы видите блок кода в функции, которой нужен комментарий, объясняющий, что он делает, заключите его в отдельную функцию с описательным именем. Даже если это всего несколько строк, и даже если он используется только один раз. Эта практика устраняет большинство проблем с перерывом / продолжением или многократным возвратом.
Дима

2
@Mikhail: Цикломатическая сложность, как правило, довольно сильно коррелирует с SLOC, что означает, что совет можно упростить до «не пишите длинные функции».
Джон Р. Штром

3
Идея единой точки выхода широко неверно истолкована. Давным-давно функциям не нужно было возвращать своих вызывающих. Они могут вернуться в другое место. Это обычно делалось на ассемблере. У Фортрана была специальная конструкция для этого; Вы можете передать номер оператора, которому предшествует амперсанд CALL P(X, Y, &10), и в случае ошибки функция может передать управление этому оператору вместо возврата к точке вызова.
Кевин Клайн

@kevincline, как видно с Луа, например.
Qix

1
@cjsimon ты понял. Не только функции должны быть маленькими. Классы тоже должны быть маленькими.
Дима

39

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

Использование break и continue часто затрудняет отслеживание кода. Но если их замена делает код еще сложнее для понимания, это плохое изменение.

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


Да, я преувеличил количество условий, чтобы привести примеры случаев выхода.
Михаил

9
Какой пример кода замены вы предложили? Я думал, что это был довольно разумный пример защитных заявлений.
Simgineer

Мне кажется, что «самое ясное решение возможно» всегда ... возможно? Как не может быть «ясного» решения? Но тогда я не один для абсолютов, так что, возможно, вы правы.

@nocomprende Я не уверен, к чему ты клонишь. «Возможное» здесь не означает, что лучшего решения не существует - только то, что идеальная, предельная ясность не вещь. В конце концов, это субъективно.
Мэтью Прочитал

22

Большинство людей думают, что это плохая идея, потому что поведение не легко предсказуемо. Если вы читаете код и видите while(x < 1000){}, что он будет работать до тех пор, пока x> = 1000 ... Но если в середине есть перерывы, то это не так, поэтому вы не можете доверять своему зацикливание ...

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

Для себя, если бы я собирался сделать цикл, который нарушал более чем одно условие, я бы while(x){}тогда переключил X на false, когда мне нужно было выйти. Окончательный результат будет таким же, и любой, кто читает код, будет знать, что нужно присмотреться к вещам, которые изменили значение X.


2
+1 очень хорошо сказано, и +1 (если бы я мог сделать другое) для while(notDone){ }подхода.
FrustratedWithFormsDesigner

5
Михаил: Проблема с перерывом в том, что конечное условие цикла никогда не указывается просто в одном месте. Это затрудняет прогнозирование постусловия после цикла. В этом тривиальном случае (> = 1000) это не сложно. Если добавить много выражений if и использовать разные уровни вложения, то будет очень трудно определить постусловие цикла.
S.Lott

С.Лотт ударил ногтем прямо по голове. Выражение, управляющее итерацией, должно включать все условия, которые должны быть выполнены, чтобы продолжить итерацию.
bit-twiddler

6
Замена break;на x=false;не делает ваш код более понятным. Вы все еще должны искать тело для этого утверждения. И в случае, x=false;если вам нужно будет убедиться, что это не ударит x=true;дальше.
Сьорд

14
Когда люди говорят: «Я вижу x, и я предполагаю, что y, но если вы делаете z, это предположение не выполняется,« я склонен думать », поэтому не делайте этого глупого предположения». Многие люди упростили бы это до «когда я увижу, while (x < 1000)я предполагаю, что это будет работать 1000 раз». Ну, есть много причин, почему это неверно, даже если xизначально он равен нулю. Например, кто говорит, что xво время цикла увеличивается только один раз, и никогда не изменяется каким-либо другим способом? Даже по вашему собственному предположению, просто потому, что что-то установлено x >= 1000, не означает, что цикл закончится - он может быть установлен обратно в диапазон, прежде чем условие будет проверено.
Steve314

14

Да, вы можете [пере] писать программы без операторов прерывания (или возврата из середины циклов, которые делают то же самое). Но вам, возможно, придется ввести дополнительные переменные и / или дублирование кода, которые обычно затрудняют понимание программы. По этой причине Pascal (язык программирования) был очень плох, особенно для начинающих программистов. Ваш начальник в основном хочет, чтобы вы программировали в управляющих структурах Паскаля. Если бы Линус Торвальдс был на вашем месте, он, вероятно, показал бы вашему боссу средний палец!

Есть результат компьютерной науки, который называется иерархия структур управления Косараджу, которая восходит к 1973 году и упоминается в (более) известной статье Кнута о gotos от 1974 года. (Кстати, эта статья Кнута была уже рекомендована Дэвидом Торнли выше) .) То, что С. Рао Косараю доказал в 1973 году, заключается в том, что невозможно переписать все программы, имеющие многоуровневые разрывы глубины n, в программы с глубиной разрывов меньше, чем n, без введения дополнительных переменных. Но допустим, что это чисто теоретический результат. (Просто добавьте несколько дополнительных переменных ?! Конечно, вы можете сделать это, чтобы угодить своему боссу ...)

Что гораздо важнее с точки зрения разработки программного обеспечения, это более поздняя статья Эрика С. Робертса 1995 года под названием « Выход из цикла и структурированное программирование: возобновление дебатов» ( http://cs.stanford.edu/people/eroberts/papers/SIGCSE- 1995 / LoopExits.pdf ). Робертс суммирует несколько эмпирических исследований, проведенных другими до него. Например, когда группу студентов типа CS101 попросили написать код для функции, реализующей последовательный поиск в массиве, автор исследования сказал следующее о тех студентах, которые использовали break / return / goto для выхода из последовательный цикл поиска, когда элемент был найден:

Я до сих пор не нашел ни одного человека, который пытался запустить программу, используя [этот стиль], который дал неправильное решение.

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

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

Да, вы можете быть более опытным, чем учащиеся CS101, но без использования оператора break (или эквивалентного возврата / перехода из середины цикла), в конце концов вы напишите код, который, хотя номинально хорошо структурирован, достаточно сложен с точки зрения дополнительной логики Переменные и дублирование кода, что кто-то, вероятно, вы сами, добавите в него логические ошибки, пытаясь следовать стилю кодирования вашего босса.

Я также хочу сказать, что статья Робертса гораздо более доступна для среднего программиста, поэтому лучше читать ее сначала, чем книгу Кнута. Это также короче и охватывает более узкую тему. Вы могли бы даже рекомендовать это своему боссу, даже если он является менеджером, а не типом 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 Но на самом деле ваши <сравнения должны <=соответствовать решению ОП
Эль Ронноко

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

6

«Плохость» зависит от того, как вы их используете. Обычно я использую разрывы в конструкциях циклов ТОЛЬКО тогда, когда это спасет меня от циклов, которые невозможно сохранить с помощью рефакторинга алгоритма. Например, циклически просматривая коллекцию в поисках элемента со значением в определенном свойстве, установленном в значение true. Если все, что вам нужно знать, это то, что для одного из элементов это свойство установлено в значение true, как только вы достигнете этого результата, разрыв подходит для правильного завершения цикла.

Если использование разрыва не сделает код более простым для чтения, более коротким для выполнения или существенным образом сохраненным циклами обработки, то лучше их не использовать. Я стараюсь кодировать «наименьший общий знаменатель», когда это возможно, чтобы каждый, кто следует за мной, мог легко посмотреть на мой код и выяснить, что происходит (я не всегда успешен в этом). Перерывы уменьшают это, потому что они вводят нечетные точки входа / выхода. При неправильном использовании они могут вести себя очень как неуместное утверждение «goto».


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

@Mikhail: предоставленные вами образцы немного альтруистичны для определенного реализма. На мой взгляд, эти образцы четкие, лаконичные, их легче читать. Большинство петель не такие. Большинство циклов имеют какую-то другую логику, которую они выполняют, и потенциально гораздо более сложные условные выражения. Именно в этих случаях прерывание / продолжение не может быть лучшим использованием, потому что это портит логику при чтении.
Джоэл Этертон

4

Абсолютно нет ... Да, использование gotoплохо, потому что это ухудшает структуру вашей программы, а также очень трудно понять поток управления.

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

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


2
Я не использовал «continue» с тех пор, как впервые изучил C в 1981 году. Это ненужная языковая функция, поскольку код, который пропускается оператором continue, может быть обернут условным оператором управления.
битник

12
В этих случаях я предпочитаю использовать продолжить, так как он гарантирует, что мой код не станет кодом стрелки. Я ненавижу код стрелки больше, чем операторы типа goto. Я также читаю его как «если это утверждение верно, пропустите оставшуюся часть этого цикла и продолжите следующую итерацию». Очень полезно, когда оно находится в самом начале цикла for (менее полезно в циклах while).
Йтернберг

@jsternberg Для победы! :-)
Notinlist

3

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

Частично эта трудность отражается в способности концептуально рассуждать о вашем коде.

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


Когда я работаю в проекте, который менеджер обязывает их использовать, мне приходилось использовать
потоковую диаграмму,

ваш «откровенно» комментарий синтаксический - «следующий» - вещь perl; нормальные языки используют «продолжить» для обозначения «пропустить этот цикл»
Михаил

@ Мик, может быть, "пропустить эту итерацию" было бы лучшим описанием. С уважением, доктор И.М. Педантик
Пит Уилсон

@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цикл.
Эль Ронноко

1
@ Эван, хотя я понимаю вашу точку зрения - то есть «что если вам нужно выйти из цикла foreach?» - Ну, breakна мой взгляд, из цикла должен быть только один максимум.
Эль Ронноко

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

@ Пит, я также поддерживаю свое заявление о сгенерированном коде. Вы делаете еще одно сравнение, сравнивая флаг с постоянным значением, прежде чем оценивать логическое значение выражения. Зачем делать это сложнее, чем должно быть, переменная flag уже имеет желаемое значение!
Зик Ханселл

+1 хорошая работа. Определенно намного элегантнее, чем в предыдущем примере.
Эван Плейс

2

Я согласен с вашим боссом. Они плохие, потому что они производят методы с высокой цикломатической сложностью. Такие методы трудны для чтения и тестирования. К счастью, есть простое решение. Извлеките тело цикла в отдельный метод, где «continue» становится «return». «Возвращение» лучше, потому что после «возвращения» все кончено - нет беспокойства о местном штате.

Для «break» извлеките сам цикл в отдельный метод, заменив «break» на «return».

Если извлеченные методы требуют большого количества аргументов, это указание для извлечения класса - либо соберите их в объект контекста.


0

Я думаю, что это проблема только тогда, когда она вложена глубоко в несколько циклов. Трудно понять, на какую петлю вы выходите. Может быть также трудно следовать продолжению, но я думаю, что настоящая боль исходит от перерывов - может быть трудно следовать логике.


2
На самом деле, легко увидеть, правильно ли вы сделали отступ, усвоили ли семантику этих утверждений и не слишком ли вы сонливы.

@delnan - это много предположений;)
davidhaskins

2
Да, особенно последний.
Майкл К

Ну, в любом случае # 1 необходим для серьезного программирования, № 2 следует ожидать от всех, кого называют программистом, а № 3 в целом весьма полезен;)

Поддержка некоторых языков (только те, которые я знаю) break 2; для других, я думаю, используются временные флаги bool
Михаил

0

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

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

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

Я в порядке с ними. (Пример видно из производственного кода, ме)


Вы бы порекомендовали для подобных случаев создавать функции?
Михаил

Да, и если не возможно, простой goto. Когда я вижу, я думаю, что повторение, а не контекст для имитации прыжка.
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
Слишком много вложений разрушает читабельность. И иногда, избегая перерыва / продолжения, возникает необходимость инвертировать вашу логику в ваших условных тестах, что может привести к неправильной интерпретации того, что делает ваш код - я просто говорю
Зик Ханселл

0

Нет. Это способ решить проблему, и есть другие способы ее решения.

Многие текущие основные языки (Java, .NET (C # + VB), PHP, напишите свой собственный) используют «break» и «continue» для пропуска циклов. Они оба "структурированные предложения goto (s)".

Без них:

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());

Обратите внимание, что код «break» и «continue» короче и обычно превращает «while» в предложения «for» или «foreach».

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

Я на самом деле работаю в некоторых проектах, где было обязательно использовать эти предложения.

Некоторые разработчики могут подумать, что они не являются необходимыми, но гипотетическими, если мы должны были удалить их, мы должны также удалить «while» и «do while» («повторить до», вы, ребята, паскали);

Заключение, даже если я предпочитаю не использовать их, я думаю , что это вариант, а не плохая практика программирования.


Извините за придирчивость, но во втором примере отсутствует вывод, когда ключ не найден (поэтому, конечно, он выглядит намного короче).
FrustratedWithFormsDesigner

2
не говоря уже о том, что первый пример работает, только если ключ является последним в списке.
Михаил

@FrustratedWithFormsDesigner ТОЧНО. Я ставлю на purpouse, чтобы обозначить, почему предпочтительнее этот метод ;-)
umlcat

однако у вас есть две подпрограммы с различной семантикой; следовательно, они не являются логически эквивалентными.
bit-twiddler

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);

Возможно, вызовы Where и Take даже не должны быть в этом методе. Возможно, эту фильтрацию следует выполнить ДО того, как коллекция будет передана этому методу. В любом случае, отойдя от низкоуровневых вещей и сконцентрировавшись больше на реальной бизнес-логике, становится все более ясным, чем мы на самом деле интересуемся. Становится легче разделить наш код на связные модули, которые в большей степени придерживаются хороших методов проектирования и т. на.

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


-1

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

В целом это неплохая практика. breakили continueскажи точно, что будет дальше. И я с этим согласен.

Стив Макконнелл (автор Code Complete) использует почти те же примеры, что и вы, чтобы показать преимущества использования различных gotoоператоров.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.