Должен ли я переместить условие разрыва в поле условия, если это возможно, внутри цикла for? [закрыто]

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
}

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

ocomfd
источник
7
Зависит от того, что conditionконкретно.
Берги
4
Существует причина, по которой Мисра запрещает использование «разрыва» и «продолжения» внутри циклов. Смотрите также это: 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: Если переписать это не вариант, то на самом деле больше нет вопроса. Сказав, что вы обычно можете довольно безопасно использовать рефактор типа «Extract Method» для перемещения блоков кода, что должно сделать ваш цикл основного метода короче и, как мы надеемся, сделать логический поток более очевидным. Это действительно в каждом конкретном случае. Я буду придерживаться своего общего правила о том, что методы должны быть короткими и определенно держать логику следующей петли короткой, но я не хочу пытаться говорить что-то большее, чем это в общем смысле ...
Крис
4
Краткость @AndrewHenle - удобочитаемость, по крайней мере, в том смысле, что увеличение многословия всегда снижает удобочитаемость и удобство обслуживания. Сокращение LOC достигается за счет увеличения плотности и повышения уровня абстракции, и оно очень тесно связано с ремонтопригодностью, что подтверждается в других парах Q / A на этом самом сайте.
Леушенко
5
@Joe конструкция Do-While настолько редка, что склонна к путанице с разработчиками, которым необходимо поддерживать этот код в дальнейшем. Несколько разработчиков никогда их не видели! Я не уверен , что делать-то время позволит улучшить читаемость на всех - во всяком случае, было бы увеличить трудность понимания кода. Просто в качестве примера - моей текущей кодовой базе около 15 лет и около 2 миллионов LOC. Около пятидесяти разработчиков уже коснулись этого. У него ровно ноль циклов do-while!
Т. Сар - Восстановить Монику
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
Вы не можете просто поместить условие разрыва в цикл, если за ним следует код, поэтому, по крайней мере, вам придется написать «if (условие) {найдено = true; продолжить;}
gnasher729
Мы настолько привыкли к for(int i=0; i<something;i++)циклам, что, полагаю, половина разработчиков на самом деле не помнят, что forциклы можно использовать по-разному, и, следовательно, им сложно разобраться в &&conditionчасти.
Ральф Клеберхофф
@RalfKleberhoff Действительно. Я видел много новых разработчиков, выражавших удивление, что все выражения оператора tripart являются необязательными. Я даже не помню, когда в последний раз видел for(;;)...
Робби Ди
43

forЦиклы предназначены для итерации по чему-то 1 - они не просто заголовки, которые можно найти в заголовке - три выражения имеют очень конкретные значения:

  • Первый предназначен для инициализации итератора . Это может быть индекс, указатель, объект итерации или что-то еще - при условии, что он используется для итерации .
  • Второй - для проверки, достигли ли мы конца.
  • Третий - для продвижения итератора.

Идея состоит в том, чтобы отделить обработку итераций ( как выполнять итерацию) от логики внутри цикла ( что делать в каждой итерации). Во многих языках обычно есть цикл for-each, который освобождает вас от деталей итерации, но даже если ваш язык не имеет такой конструкции или если ее нельзя использовать в вашем сценарии - вы все равно должны ограничить заголовок цикла к итерационной обработке.

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

1 В отличие от других циклов, которые «ждут», чтобы что-то произошло. Цикл for/ foreachдолжен иметь концепцию «списка» элементов для итерации, даже если этот список ленивый, бесконечный или абстрактный.

Идан Арье
источник
5
Это была моя первая мысль, когда я прочитал вопрос. Разочарован, мне пришлось пролистать полдюжины ответов, чтобы увидеть, как кто-то говорит это
Nemo
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если это возможно
  • Сделайте условной частью условия цикла for
  • Разместите условные обозначения breakв верхней части цикла, если это возможно
  • Добавьте многострочный комментарий, чтобы обратить внимание на разрыв и почему он там есть.

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

Берин Лорич
источник
1
Большая часть этого ответа в порядке. Но ... я вижу, как фильтрация коллекции может устранить необходимость в continueутверждениях, но я не понимаю, как они сильно помогают break.
user949300
1
@ user949300 takeWhileФильтр может заменить breakоператоры. Если у вас есть один - Java, например , только получает их в Jave 9 (не то, что это , что трудно реализовать его самостоятельно)
Идан Арье
1
Но в Java вы все еще можете фильтровать перед итерацией. Используя потоки (1.8 или позже) или используя Коллекции Apache (1.7 или ранее).
Laiv
@IdanArye - есть дополнительные библиотеки, которые добавляют такие возможности в API потоков Java (например, JOO-лямбда )
Jules
@IdanArye никогда не слышал о дубле до вашего комментария. Явное связывание фильтра с упорядочением кажется мне необычным и хакерским, поскольку в «нормальном» фильтре каждый элемент возвращает true или false, независимо от того, что происходит до или после. Таким образом, вы можете запускать вещи параллельно.
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
}
Caleth
источник
4

TL; DR. Делай то, что лучше всего в твоих конкретных обстоятельствах.

Давайте возьмем этот пример:

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

В этом случае мы не хотим, чтобы какой-либо код в цикле for выполнялся, если он somethingравен true, поэтому перенос теста 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условие?

жесткий
источник
Что такое " openion "? Вы имеете в виду « мнение »?
Питер Мортенсен
Что ты имеешь в виду под «Множество причин, которые говорят, что не должны» ? Можете ли вы уточнить?
Питер Мортенсен
0

Я думаю, что удаление breakможет быть хорошей идеей, но не для сохранения строк кода.

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

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

Это было первоначальной причиной, по которой Эдгар Дейкстра написал «Goto считает опасным». Это не было вопросом стиля: нарушение цикла затрудняет формальное рассуждение о состоянии программы. Я написал множество break;утверждений в своей жизни, и однажды, будучи взрослым, я даже написал goto(чтобы разорвать несколько уровней критичного к производительности внутреннего цикла и затем перейти к другой ветви дерева поиска). Тем не менее, у меня гораздо больше шансов написать условный returnоператор из цикла (который никогда не запускает код после цикла и, следовательно, не может испортить его постусловия), чем a break.

постскриптум

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

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

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

Я признаю, что это был не основной вопрос вашего вопроса, и вы держали его в простоте.

Davislor
источник
Проблема не в том, что из-за разрыва трудно спорить о коде, а из-за того, что разрыв в случайных местах превращает код в сложные вещи. Если это действительно необходимо, то спорить не сложно из-за перерыва, а потому, что код должен делать сложные вещи.
gnasher729
Вот почему я не согласен: если вы знаете, что нет breakзаявления, то вы знаете, что такое условие цикла и что цикл останавливается, когда это условие ложно. Если есть break? Тогда есть несколько способов, которыми цикл может завершиться, и в общем случае выяснение, когда один из них может остановить цикл, буквально решает проблему остановки. На практике меня больше беспокоит то, что цикл выглядит так, как будто он делает одно, но на самом деле тот, кто написал код после цикла, пропустил крайний случай, скрытый в его сложной логике. Вещество в петле трудно пропустить.
Дэвислор
0

Да, но ваш синтаксис нетрадиционен и, следовательно, плох (тм)

Надеюсь, ваш язык поддерживает что-то вроде

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
}
Ewan
источник
2
возможно, я должен был использовать другое слово для «условия», я не имел в виду буквально, чтобы означать точно такое же условие в примере
Ewan
1
Цикл for не только не является даже нетрадиционным, но и абсолютно без причины, поскольку цикл while этой формы лучше, чем эквивалент цикла. Фактически большинство людей сказали бы, что это хуже, таких как авторы Основных руководящих принципов CPP
Шон Бертон
2
Некоторые люди просто ненавидят творческие альтернативы. Или смотреть на проблему иначе, чем тот, который задан в их головах. Или, в целом, умные ослы. Я чувствую твою боль, братан!
Мартин Маат
1
@ sean извини чувак, я знаю, что людям не нравится, когда им говорят, что они не правы, но я отсылаю тебя к es.77
Ewan
2
Это whileподчеркивает монстра в коде - исключение, скрытое в обычном / обычном коде. Бизнес-логика находится впереди и в центре, механика циклов включена. Это позволяет избежать реакции «подожди минуту ...» на forальтернативу цикла. Этот ответ решает проблему. абсолютно голосование.
радар Боб
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 (i = 0; i < something && ! found; ++i)

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

if (! found) { ... }

где-то в вашей петле.

gnasher729
источник