Подтвердить что ты не робот

Почему мы используем 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;
}

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

4b9b3361

Ответ 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!");
}

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