Для "if, if, if" или "if, else if, else if, else"
Я пишу код для анализа данных и должен исключать образцы на основе некоторых критериев. На практике я получаю код, например:
bool Test(SampleType sample)
{
if( ! SubTest1(sample) )
return false;
if( ! SubTest2(sample) )
return false;
if( ! SubTest3(sample) )
return false;
return true;
}
Мне кажется эквивалентным следующее:
bool Test(SampleType sample)
{
if( ! SubTest1(sample) )
return false;
else if( ! SubTest2(sample) )
return false;
else if( ! SubTest3(sample) )
return false;
else
return true;
}
Есть ли разница в стоимости вычислений? Существует ли спорный преференциальный вариант с точки зрения расширяемости/ремонтопригодности, эстетики и т.д.??
Я знаю, что это, вероятно, несущественная проблема, но как только я получаю эти вопросы, застрявшие у меня в голове, мне нужно найти ответ.
PS: в случае, если кто-то заботится, мой фактический код от 15/09 можно найти на следующем:
http://folk.uio.no/henrikq/conf.tgz
Ответы
Ответ 1
Компилятор генерирует одинаковый код для обеих версий. Но первая версия лучше в аспекте поддерживаемости, если сравнить только со второй версией.
Код завершается, когда встречается оператор return
; поэтому в предстоящем if
не используется сохранение else
. Это заставляет разработчика лучше понимать код.
Кроме того, если это буквальный код, вы все равно можете сжимать как
bool Test(SampleType sample)
{
return (SubTest1(sample) && SubTest2(sample) && SubTest3(sample));
}
Ответ 2
Я бы сделал это:
return SubTest1(sample) && SubTest2(sample) && SubTest3(sample);
Это не улучшит производительность, но то, что делает эта функция, может (или не обязательно) быть более очевидным.
Ответ 3
Оба они полностью эквивалентны по своим характеристикам, и компиляторы, вероятно, испускают одинаковый машинный код для обоих.
Они даже не так сильно отличаются с точки зрения удобочитаемости - обе имеют одинаковое количество гнездования. Сравните это с тем случаем, когда раннее возвращение не приводит к глубокой вложенности.
Ответ 4
С точки зрения удобочитаемости,
bool Test( SampleType sample )
{
return SubTest1( sample )
&& SubTest2( sample )
&& SubTest3( sample );
}
намного яснее любого из ваших вариантов. В противном случае, вы определенно
хотите, чтобы else
дал понять, что как только одно из условий
были выполнены, никто из других не будет проверен.
Ответ 5
Оба идентичны. Компилятор, скорее всего, выяснит, как оптимизировать их, чтобы испускался идентичный машинный код. Я бы выбрал первый код, так как его легче читать.
Ответ 6
Я не могу себе представить, что это повлияет на производительность.
Я бы использовал первый, если они были независимыми тестами, и вы просто хотите выбыть после того, как один из них преуспеет, а второй - при выборе между набором логических альтернатив.
Ответ 7
Насколько я знаю, нет никакой разницы в приведенном машинный код. Кроме того, кого это волнует? У вас есть проблема с этим кодом? Производительность - сложный вопрос, который обычно должен быть оставлен до конца проекта, и только если есть проблемы с производительностью, они должны быть найдены и зафиксированы ГДЕ ОНИ ОСАГО СУЩЕСТВУЮТ, а не там, где вы думаете заранее,.
Ответ 8
В основном это зависит от того, как компилятор оптимизирует полученный код.
if (.) обычно переводится с помощью команды compare_to_zero, за которой следует условный переход.
В вашем случае это будет
CMP(c1)
RETZ
CMP(c2)
RETZ
CMP(c3)
RETZ
или (с помощью else-s)
CMP(c1)
JNZ(a1)
RET
a1:CMP(c2)
JNZ(a2)
RET
a2:CMP(c3)
JNZ(a3)
RET
a3:RET
Второй прогон оптимизатора увидит цепочку прыжков, инвертирует then/else (и Z/NZ), пропускает прыжки, находясь в точке "прыжок в следующий", давая точно предыдущий код.
Ответ 9
Это зависит от языка и окружающих API
Первое решение является типичным императивным подходом, основное внимание уделяется действию проверки чего-либо: прерывание, если a, прервать, если b, прервать, если c, успех.
Второе решение является более функциональным aproach, его можно считать фигурной скобкой в математических определениях (как в: fac(n) = { n <= 1: 1, n > 1: n * fac(n-1)
).
Решение должно быть выбрано в соответствии с окружающей средой. В Java, Python и т.д. Он будет первым. В рубине вторая может быть прекрасной.
Ответ 10
существует хорошая практика, которая называется "одна точка входа, одна точка выхода". Это означает, что лучше иметь только один возврат в функции.
На данный момент ваша функция проста, но в будущем она может расти, усложняться, выделять временные переменные, которые необходимо освободить, и т.д.
Поэтому лучше использовать лучшие практики во всем мире. Он назывался "защитное программирование", защита от человеческих неудач (моя и мои коллеги).
Я бы написал это так:
bool Test(SampleType sample)
{
bool bRet = true; // by default, set it to the most "defensive" value which will cause the less harm or make the problem evident
// in the future, you may have inits and allocations here
if ( !SubTest1(sample) )
bRet = false; // don't match
else if ( !SubTest2(sample) )
bRet = false; // don't match
else if ( !SubTest3(sample) )
bRet = false; // don't match
else
; // bRet stays true
// thanks to the single point of exit, you can do things like that
if (bRet)
log("... match");
else
log("...no match")
// here you clean temporary resources...
return bRet;
}
Если вы хотите повысить производительность, лучший способ - установить функции SubTestX в лучшем порядке, чтобы первый, который не соответствует большинству, во-первых, поэтому требуется меньше тестов, чтобы найти, что оно не соответствует.