Рассмотрим следующий код:
public void doSomething(int input)
{
while(true)
{
TransformInSomeWay(input);
if(ProcessingComplete(input))
break;
DoSomethingElseTo(input);
}
}
Предположим, что этот процесс включает в себя конечное, но зависящее от ввода число шагов; Цикл предназначен для самостоятельного завершения в результате работы алгоритма и не рассчитан на бесконечное выполнение (до тех пор, пока не будет отменено внешним событием). Поскольку проверка того, должен ли цикл завершиться, находится в середине логического набора шагов, сам цикл while в настоящее время не проверяет ничего значащего; вместо этого проверка выполняется в «правильном» месте концептуального алгоритма.
Мне сказали, что это плохой код, потому что он более подвержен ошибкам из-за того, что конечное условие не проверяется структурой цикла. Сложнее понять, как вы выйдете из цикла, и можете вызвать ошибки, поскольку условие прерывания может быть случайно обойдено или пропущено с учетом будущих изменений.
Теперь код можно структурировать следующим образом:
public void doSomething(int input)
{
TransformInSomeWay(input);
while(!ProcessingComplete(input))
{
DoSomethingElseTo(input);
TransformInSomeWay(input);
}
}
Однако это дублирует вызов метода в коде, нарушая DRY; если TransformInSomeWay
впоследствии они будут заменены каким-либо другим методом, оба вызова необходимо будет найти и изменить (и тот факт, что их два, может быть менее очевидным в более сложном фрагменте кода).
Вы также можете написать это так:
public void doSomething(int input)
{
var complete = false;
while(!complete)
{
TransformInSomeWay(input);
complete = ProcessingComplete(input);
if(!complete)
{
DoSomethingElseTo(input);
}
}
}
... но теперь у вас есть переменная, единственная цель которой - сдвинуть проверку условий в структуру цикла, а также должна быть проверена несколько раз, чтобы обеспечить то же поведение, что и исходная логика.
Со своей стороны, я говорю, что, учитывая алгоритм, который этот код реализует в реальном мире, исходный код является наиболее читабельным. Если бы вы проходили через это сами, вы бы так об этом подумали, и это было бы интуитивно понятно людям, знакомым с алгоритмом.
Так что же лучше? лучше ли возложить ответственность за проверку условий на цикл while, структурируя логику вокруг цикла? Или лучше структурировать логику «естественным» образом, как указано требованиями или концептуальным описанием алгоритма, даже если это может означать обход встроенных возможностей цикла?
do ... until
Конструкция также полезна для этих видов петель , так как они не должны быть «загрунтовать» .Ответы:
Придерживайтесь своего первого решения. Циклы могут стать очень сложными, и один или несколько чистых разрывов могут быть намного проще для вас, для любого, кто смотрит на ваш код, оптимизатор и производительность программы, чем для сложных операторов if и добавленных переменных. Мой обычный подход - создать цикл с чем-то вроде «while (true)» и получить лучшее кодирование, какое только можно. Часто я могу и действительно заменяю разрыв (ы) стандартным циклическим управлением; В конце концов, большинство петель довольно просты. Конечное условие должно проверяться структурой цикла по указанным вами причинам, но не за счет добавления целых новых переменных, добавления операторов if и повторяющегося кода, которые все еще более подвержены ошибкам.
источник
Это только значимая проблема, если тело цикла нетривиально. Если тело такое маленькое, то анализировать его так же просто, и это совсем не проблема.
источник
У меня проблемы с поиском других решений лучше, чем с перерывом. Ну, я предпочитаю способ Ада, который позволяет делать
но решение разрыва - самый чистый рендеринг.
Мой POV заключается в том, что когда отсутствует структура управления, самое чистое решение - эмулировать ее с другими структурами управления, не используя поток данных. (И точно так же, когда у меня возникает проблема с потоком данных, я предпочитаю использовать решения с чистым потоком данных, а не те, которые включают управляющие структуры *).
Не заблуждайтесь, обсуждение не состоялось бы, если бы сложность не была в основном одинаковой: состояние одинаковое и присутствует в одном и том же месте.
Какой из
а также
сделать лучше предполагаемую структуру? Я голосую за первое, вам не нужно проверять соответствие условий. (Но посмотрите мой отступ).
источник
while (true) и break - распространенная идиома. Это канонический способ реализации циклов среднего выхода в C-подобных языках. Добавление переменной для хранения условия выхода и if () вокруг второй половины цикла является разумной альтернативой. Некоторые магазины могут официально стандартизировать один или другой, но ни один из них не превосходит; они эквивалентны.
Хороший программист, работающий на этих языках, должен быть в состоянии узнать, что происходит с первого взгляда, если он или она увидит любой из них. Это может сделать хороший маленький вопрос для интервью; Вручите кому-нибудь две петли, по одной используя каждую идиому, и спросите их, в чем разница (правильный ответ: «ничего», возможно с добавлением «но я обычно использую ЭТУ одну»).
источник
Ваши альтернативы не так плохи, как вы думаете. Хороший код должен:
Четко укажите с хорошими именами переменных / комментариями, при каких условиях цикл должен быть прекращен. (Условие разрушения не должно быть скрыто)
Четко укажите, каков порядок выполнения операций. Это где дополнительная 3-я операция (
DoSomethingElseTo(input)
) внутри if является хорошей идеей.Тем не менее, легко позволить перфекционистским инстинктам полировки латуни отнимать много времени. Я бы просто настроил, если, как упоминалось выше; закомментируйте код и двигайтесь дальше.
источник
Люди говорят, что это плохая практика
while (true)
, и большую часть времени они правы. Если вашеwhile
утверждение содержит много сложного кода и неясно, когда вы отказываетесь от if, тогда вам остается трудно поддерживать код, и простая ошибка может вызвать бесконечный цикл.В вашем примере вы правы в использовании,
while(true)
потому что ваш код прост и понятен. Нет смысла создавать неэффективный и сложный для чтения код просто потому, что некоторые люди считают, что это всегда плохая практикаwhile(true)
.Я столкнулся с похожей проблемой:
Использование
do ... while(true)
позволяет избежатьisset($array][$key]
ненужного повторного вызова, код становится короче, чище и проще для чтения. Нет абсолютно никакого риска появления ошибки бесконечного циклаelse break;
в конце.Хорошо следовать хорошей практике и избегать плохой практики, но всегда есть исключения, и важно сохранять непредвзятость и осознавать эти исключения.
источник
Если конкретный поток управления естественным образом выражается как оператор перерыва, по сути, никогда лучше не использовать другие механизмы управления потоком для эмуляции оператора перерыва.
(обратите внимание, что это включает в себя частичное развертывание цикла и скольжение тела цикла вниз, так что начало цикла совпадает с условным тестом)
Если вы можете реорганизовать свой цикл так, чтобы поток управления был естественным образом выражен условной проверкой в начале цикла, то это здорово. Возможно, стоит даже подумать о том, возможно ли это. Но это не так, не пытайтесь вставить квадратный колышек в круглое отверстие.
источник
Вы также можете пойти с этим:
Или менее запутанно:
источник
Когда сложность логики становится громоздкой, тогда использование неограниченного цикла с разрывами в середине цикла для управления потоком действительно имеет смысл. У меня есть проект с некоторым кодом, который выглядит следующим образом. (Обратите внимание на исключение в конце цикла.)
Чтобы сделать эту логику без прерывания неограниченного цикла, я хотел бы получить что-то вроде следующего:
Таким образом, исключение теперь выдается вспомогательным методом. Также у этого есть довольно много
if ... else
вложений. Я не удовлетворен таким подходом. Поэтому я думаю, что первая модель лучше.источник