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

Для "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

4b9b3361

Ответ 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 в лучшем порядке, чтобы первый, который не соответствует большинству, во-первых, поэтому требуется меньше тестов, чтобы найти, что оно не соответствует.