Почему мы используем if, else, если вместо множественного блока if, если тело является оператором return
Я всегда привык использовать if, else-if statement вместо нескольких операторов if.
Пример:
int val = -1;
if (a == b1) {
return c1;
} else if (a == b2) {
return c2;
} ...
...
} else {
return c11;
}
Как он сравнивается с примером 2:
if (a == b1) {
return c1;
}
if (a == b2) {
return c2;
}
....
if (a == b11) {
return c11;
}
Я знаю, что функциональность мудрая одинакова. Но лучше ли это делать, если иначе - если или нет? Он поднял один из моих друзей, когда я указал, что он может структурировать базу кода по-другому, чтобы сделать его более чистым. Это уже привычка для меня надолго, но я никогда не спрашивал, почему.
Ответы
Ответ 1
Операторы if-elseif-else
перестают выполнять сравнения, как только обнаруживают, что это правда. if-if-if
делает каждое сравнение. Первый более эффективен.
Изменить: В комментариях указано, что вы выполняете return
в каждом блоке if
. В этих случаях или в тех случаях, когда управление покидает метод (исключения), нет никакой разницы между выполнением нескольких операторов if
и выполнением операторов if-elseif-else
.
Однако лучше всего использовать if-elseif-else
. Предположим, вы изменили свой код таким образом, чтобы вы не делали return
в каждом блоке if
. Затем, чтобы оставаться эффективными, вам также нужно будет перейти на идиому if-elseif-else
. Если он будет if-elseif-else
с самого начала, он сохранит ваши изменения в будущем и станет более ясным для людей, читающих ваш код (свидетельствуйте неверное истолкование, которое я вам дал, выполнив код вашего кода!).
Ответ 2
Как насчет случая, когда b1 == b2
? (А если a == b1
и a == b2
?)
Когда это происходит, вообще говоря, следующие два куска кода могут иметь другое поведение:
if (a == b1) {
/* do stuff here, and break out of the test */
}
else if (a == b2) {
/* this block is never reached */
}
и
if (a == b1) {
/* do stuff here */
}
if (a == b2) {
/* do this stuff, as well */
}
Если вы хотите четко разграничить функциональность для разных случаев, используйте if-else
или switch-case
, чтобы выполнить один тест.
Если вы хотите разную функциональность для нескольких случаев, используйте несколько блоков if
в качестве отдельных тестов.
Это не вопрос "лучших практик", а определение того, есть ли у вас один тест или несколько тестов.
Ответ 3
НЕ функционально эквивалентны.
Единственный способ, который был бы функционально эквивалентен, заключался в том, что вы выполнили оператор "if" для каждого единственного возможного значения a (т.е.: каждое возможное значение int, как определено в limits.h в C, с использованием INT_MIN и INT_MAX, или эквивалентно в Java).
Оператор else позволяет вам покрыть все возможные оставшиеся значения без необходимости писать миллионы операторов if.
Кроме того, лучше использовать практику кодирования, если... else if... else, как и в случае с оператором switch/case, ваш компилятор будет называть вас предупреждением, если вы не предоставите "default", дело. Это не позволяет вам игнорировать недопустимые значения в вашей программе. например:
double square_root(double x) {
if(x > 0.0f) {
return sqrt(x);
} else if(x == 0.0f) {
return x;
} else {
printf("INVALID VALUE: x must be greater than zero");
return 0.0f;
}
}
Вы хотите набирать миллионы операторов if для каждого возможного значения x в этом случае? Сомневаюсь:)
Ура!
Ответ 4
Я склонен думать, что использование else if
более прост в сравнении с изменениями кода. Если кто-то должен был отрегулировать поток управления функции и заменяет возврат с помощью побочного эффекта или вызова функции с помощью try-catch
, else-if
будет терпеть неудачу, если все условия действительно исключительны. На самом деле это зависит от конкретного кода, с которым вы работаете, чтобы сделать общее мнение, и вам нужно кратко рассмотреть возможные компромиссы.
Ответ 5
С операторами return
в каждой ветки if
.
В вашем коде у вас есть операторы return
в каждом из условий if. Когда у вас есть такая ситуация, есть два способа написать это. Во-первых, как вы написали его в примере 1:
if (a == b1) {
return c1;
} else if (a == b2) {
return c2;
} else {
return c11;
}
Другое следующее:
if (a == b1) {
return c1;
}
if (a == b2) {
return c2;
}
return c11; // no if or else around this return statement
Эти два способа написания кода идентичны.
То, как вы написали код в примере 2, не будет компилироваться в С++ или Java (и будет undefined поведение в C), потому что компилятор не знает, что вы охватили все возможные значения a
поэтому он считает, что существует путь кода через функцию, которая может довести вас до конца функции без возврата возвращаемого значения.
if (a == b1) {
return c1;
}
if (a == b2) {
return c2;
}
...
if (a == b11) {
return c11;
}
// what if you set a to some value c12?
Без операторов return
в каждой ветки if
.
Без операторов return
в каждой ветки if
, ваш код будет функционально идентичным, только если выполняются следующие утверждения:
- Вы не изменяете значение
a
в любом из ветвей if
.
-
==
- отношение эквивалентности (в математическом смысле), и ни один из b1
thru b11
не находится в одном классе эквивалентности.
-
==
не имеет побочных эффектов.
Далее прояснить точку № 2 (а также точку № 3):
-
==
всегда является отношением эквивалентности в C или Java и никогда не имеет побочных эффектов.
- В языках, которые позволяют вам переопределить оператор
==
, такой как С++, Ruby или Scala, переопределенный оператор ==
может не быть отношением эквивалентности и может иметь побочные эффекты. Мы, конечно, надеемся, что тот, кто переопределяет оператор ==
, был достаточно здравым, чтобы написать отношение эквивалентности, которое не имеет побочных эффектов, но нет гарантии.
- В JavaScript и некоторых других языках программирования со свободными правилами преобразования типов существуют случаи, когда язык
==
не является транзитивным или несимметричным. (В Javascript ===
- отношение эквивалентности.)
С точки зрения производительности, пример № 1 гарантированно не выполняет никаких сравнений после того, который соответствует. Возможно, компилятор сможет оптимизировать # 2, чтобы пропустить дополнительные сравнения, но это маловероятно. В следующем примере это, вероятно, не может, и если строки длинны, дополнительные сравнения не являются дешевыми.
if (strcmp(str, "b1") == 0) {
...
}
if (strcmp(str, "b2") == 0) {
...
}
if (strcmp(str, "b3") == 0) {
...
}
Ответ 6
Я предпочитаю, если /else структуры, потому что гораздо легче оценить все возможные состояния вашей проблемы в каждой вариации вместе с переключателями. Это более надежное решение, которое я нахожу и быстрее отлаживаю, особенно когда вы выполняете несколько булевых вычислений в слабо типизированной среде, например PHP, например, почему elseif является плохим (преувеличенным для демонстрации):
if(a && (c == d))
{
} elseif ( b && (!d || a))
{
} elseif ( d == a && ( b^2 > c))
{
} else {
}
Эта проблема выходит за пределы 4 ^ 2 = 16 булевых состояний, что просто демонстрирует эффекты слабого ввода, что еще хуже. Не так сложно представить три переменные состояния, три переменные проблемы, связанные с типом if ab elseif bc
.
Оставьте оптимизацию для компилятора.
Ответ 7
if
и else if
отличается от двух последовательных операторов if
. В первом случае, когда CPU берет первую ветвь if
, else if
не будет проверяться. В двух последовательных операциях if
, даже если первый if
проверен и принят, следующий if
также будет проверяться и принимать, если условие истинно.
Ответ 8
Я думаю, что эти фрагменты кода эквивалентны по той простой причине, что у вас есть много операторов return
. Если у вас есть один оператор return
, вы бы использовали конструкторы else
, которые здесь не нужны.
Ответ 9
Ваше сравнение опирается на то, что тело операторов if возвращает управление из метода. В противном случае функциональность будет отличаться.
В этом случае они выполняют те же функции. Последнее гораздо легче читать и понимать, на мой взгляд, и будет моим выбором в том, что использовать.
Ответ 10
Они потенциально могут делать разные вещи.
Если a
равно b1
и b2
, вы вводите два блока if
. В первом примере вы только когда-либо вводите его. Я предполагаю, что первый пример выполняется быстрее, поскольку компилятор, вероятно, должен проверять каждое условие последовательно, поскольку к объекту могут применяться определенные правила сравнения. Возможно, они смогут оптимизировать их... но если вы хотите, чтобы один был введен, первый подход более очевидный, с меньшей вероятностью приведет к ошибке разработчика или неэффективному коду, поэтому я определенно рекомендую это.
Ответ 11
Ответ CanSpice правильный. Дополнительным соображением для эффективности является выяснение того, какое условие происходит чаще всего. Например, если a == b1 возникает только 1% времени, то вы получаете лучшую производительность, сначала проверяя другой случай.
Gir Loves Tacos ответ тоже хорош. Лучшая практика заключается в том, чтобы обеспечить охват всех случаев.
Ответ 12
Это полностью зависит от состояния, которое вы тестируете. В вашем примере это не будет иметь никакого значения в конце концов, но, как наилучшая практика, если вы хотите, чтобы ОДИН из условий был в конечном счете выполнен, вам лучше использовать if else
if (x > 1) {
System.out.println("Hello!");
}else if (x < 1) {
System.out.println("Bye!");
}
Также обратите внимание, что если первое условие TRUE, второе не будет проверено вообще, но если вы используете
if (x > 1) {
System.out.println("Hello!");
}
if (x < 1) {
System.out.println("Bye!");
}
Второе условие будет проверено, даже если первое условие имеет значение ИСТИНА. В конечном итоге это может решить оптимизатор, но насколько я знаю, он ведет себя так. Также первый из них - тот, который должен быть написан и ведет себя так, поэтому он всегда лучший выбор для меня, если логика не требует иного.