Более эффективно использовать if-return-return или if-else-return?

154

Предположим, у меня есть ifутверждение с return. С точки зрения эффективности, следует ли мне использовать

if(A > B):
    return A+1
return A-1

или

if(A > B):
    return A+1
else:
    return A-1

Что лучше выбрать при использовании компилируемого языка (C) или скриптового (Python)?

Хорхе Лейтао
источник
12
На компилируемом языке вам не нужно сильно беспокоиться об эффективности. Компилятор разбирается в этом. Вы должны написать свой код, чтобы вы могли его прочитать. (Вам все равно придется беспокоиться об эффективности ваших алгоритмов, а небрежное использование типов и т. Д. Повлияет на эффективность - вам просто не нужно слишком беспокоиться о своем стиле.) Однако я не знаю о Python.
AMS
5
Полагаться на компилятор для сортировки кода - опасный шаг, и для него требуется надежный компилятор. Лучше, если вы знаете, чего вы хотите от вашего кода!
Эндрю
1
Если то, что вы делаете, определяется спецификацией, я не верю, что есть какие-либо причины сомневаться в компиляторе. Это будет написано людьми намного умнее вас, и гораздо более вероятно, что вы ошиблись, чем они.
будет
8
Как это можно закрыть на основании мнения? Это может быть мнение после того, как вы узнаете, что между ними нет разницы в производительности. Я не делал, и я почти уверен, что многие люди тоже этого не сделали.
Хорхе Лейтао
1
Хотя этот вопрос довольно популярен, на него нельзя ответить точно, не имея в виду конкретный язык, иначе ответ для каждого языка был бы слишком длинным для этого формата.
Эмиль Бержерон

Ответы:

206

Поскольку returnоператор завершает выполнение текущей функции, эти две формы эквивалентны (хотя вторая, возможно, более читабельна, чем первая).

Эффективность обеих форм сопоставима, базовый машинный код должен выполнить переход, если ifусловие все равно ложно.

Обратите внимание, что Python поддерживает синтаксис, который позволяет вам использовать только один returnоператор в вашем случае:

return A+1 if A > B else A-1
Фредерик Хамиди
источник
35
C тоже поддерживает это. return (A>B)?A+1:A-1;Однако от написания такого кода нет абсолютно никакого выигрыша в производительности . Все, что мы достигли, - это сделать код запутанным, нечитаемым и в некоторых случаях более уязвимым для неявного продвижения типов.
Lundin
49
@Lundin запутался? нечитаемый? Только для тех, кто не знает тернарного оператора.
glglgl
6
@Lundin Следуя этой аргументации, <это плохая практика, потому что -1 < 1uдает неожиданный результат.
glglgl
3
@glglgl: Нет, потому что люди ожидают, что оператор?: будет вести себя как if-else, что неверно. Если бы кто-то написал такой код -1 < 1u, в чем я сомневаюсь, он легко обнаружил бы ошибку. Однако довольно много людей напишут ту или иную версию кода, который я опубликовал. Я слишком часто встречал такие ошибки в производственном коде, чтобы доверять оператору? :. Также, как показывает опыт, если язык дает вам два разных способа сделать одно и то же, используйте только один из них, не выбирайте случайно ни один из двух в зависимости от вашего настроения.
Lundin
6
@Lundin - это аргумент в пользу осторожности с?: В C, но вы, кажется, говорите, что это применимо и к Python. Можете ли вы указать на какие-либо примеры, где использование тернарного кода в Python приводит к неожиданным результатам?
lvc
33

Из руководства по стилю Chromium :

Не используйте другое после возврата:

# Bad
if (foo)
  return 1
else
  return 2

# Good
if (foo)
  return 1
return 2

return 1 if foo else 2
skeller88
источник
1
Спасибо. +1. Могу я спросить, почему после возврата не использовать еще?
Тим
1
if-else функционально эквивалентен, но многословен. Остальное не нужно.
skeller88
20
Я был удивлен, потому что первый кажется более ясным и, следовательно, лучше.
Тим
4
Вы могли бы привести веские доводы в пользу того и другого. Самым важным в этом решении IMO является согласованность в кодовой базе.
skeller88
2
в большинстве случаев вы, вероятно, обнаружите, что if-else-returnветки почти никогда не равны (если они равны, вам все равно следует провести рефакторинг; либо с помощью switchконструкции, либо для Python, перечисляя dict / используя вызываемый / и т. д.). Таким образом, почти все if-else-returnявляются случаями защитных предложений, и они всегда проверяются (имитируют проверяемое выражение) без использования else.
Cowbert
6

Что касается стиля кодирования:

Большинство стандартов кодирования, независимо от языка, запрещают множественные операторы возврата из одной функции как плохую практику.

(Хотя лично я бы сказал, что есть несколько случаев, когда несколько операторов return имеют смысл: парсеры протокола текста / данных, функции с обширной обработкой ошибок и т. Д.)

Все эти отраслевые стандарты кодирования пришли к единому мнению, что выражение должно быть записано как:

int result;

if(A > B)
{
  result = A+1;
}
else
{
  result = A-1;
}
return result;

По поводу эффективности:

Приведенный выше пример и два примера в вопросе полностью эквивалентны с точки зрения эффективности. Во всех этих случаях машинный код должен сравнивать A> B, затем переходить к вычислению A + 1 или A-1, а затем сохранять результат в регистре ЦП или в стеке.

РЕДАКТИРОВАТЬ :

Источники:

  • MISRA-C: 2004, правило 14.7, которое, в свою очередь, цитирует ...:
  • МЭК 61508-3. Часть 3, таблица В.9.
  • МЭК 61508-7. C.2.9.
Лундин
источник
39
Вы уверены, что религия с одним возвращением заразила большинство стандартов кодирования? Это было бы страшно.
Дэниел Фишер
8
Я бы сказал, что правило не имеет смысла в большинстве случаев. Я стараюсь найти код более читаемым и легким для выполнения с возвратами в соответствующие моменты. Но это только я. Однако я подумал о стандартах кодирования для компании / проекта, а не о таких вещах, как MISRA, где идиотские рецепты иногда могут иметь некоторые достоинства. Я надеюсь, что большинство не поверило идее единой точки выхода.
Дэниел Фишер
3
@DanielFischer: В стандарте кодирования C, основанном на MISRA, который я разработал для своей компании, у меня есть правило «Функция должна иметь только одну точку выхода в конце функции, если только одна точка выхода не делает код менее читаемый ". Итак, это MISRA-C, но с исключением из правил. Если вы напишете расширенную функцию синтаксического анализатора, которая может возвращать, скажем, 10 различных ошибок, уровень вложенных фигурных скобок сделает код полностью нечитаемым - в таком случае было бы более разумным немедленно вернуться, если обнаружена ошибка.
Lundin
6
См. Этот вопрос SO для обсуждения и дальнейших ссылок на дальнейшие обсуждения проблемы единой точки выхода. Помимо устаревшего и чрезмерно «инженерного» правила единой точки выхода, Python специально продвигает представление «плоское лучше, чем вложенное» , и размещение return там, где это кажется ясным, является идиоматическим способом сделать это в Python.
John Y
1
@percebus Я полностью согласен, и цикломатическая сложность - хороший аргумент против однократного возврата. И я несколько раз советовал комитету MISRA по этому поводу, например, см . По крайней мере, статус правила был понижен до рекомендательного в MISRA-C: 2012.
Lundin
3

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

Оливер Чарльзуорт
источник
3

Я лично избегаю elseблоков, когда это возможно. См. Кампанию Anti-If

Кроме того, они не взимают дополнительную плату за линию, вы знаете: p

«Простое лучше, чем сложное» и «Читаемость - главное»

delta = 1 if (A > B) else -1
return A + delta
Percebus
источник
2
Почему голосование против? Это «питонический» ответ. Возможно, вы не сочтете это предпочтительным ответом. Но не недействительный. Я также следую Принципу KISS en.wikipedia.org/wiki/KISS_principle
Percebus
3
Я поддержал ваш ответ, так как для меня он очень прост и удобочитаем. Я лично считаю оскорбительным, что кто-то голосует против меня, не объясняя, почему мой ответ является отрицательным.
Стивен Эллвуд
1
Раньше не слышал о кампании «Если», но понимаю, почему «если» могут быть опасными. Я всегда стараюсь ограничить объем кода, заключенного в оператор if, и пытаюсь переписать деревья elif для использования dict. Однако это становится немного не по теме.
Стивен Элвуд
1
@StephenEllwood Использование dicts для предотвращения различий - очень плохая идея с точки зрения производительности.
Бахзау
@Bachsau Вы, наверное, правы. Мне никогда не приходилось беспокоиться о производительности, поскольку все мои сценарии выполняются за секунды. Для меня удобочитаемость обычно важнее производительности. Поскольку я не программист на полную ставку; они просто средство для достижения цели.
Стивен Элвуд
2

Это вопрос стиля (или предпочтений), так как переводчику все равно. Лично я бы попытался не делать окончательный оператор функции, которая возвращает значение на уровне отступа, отличном от базы функции. Else в примере 1 скрывает, хотя и немного, где находится конец функции.

По желанию я использую:

return A+1 if (A > B) else A-1

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

Для более сложных функций я предпочитаю разбивать функцию на несколько подфункций, чтобы избежать преждевременного возврата, если это возможно. В противном случае я возвращаюсь к использованию переменной императивного стиля под названием rval. Я стараюсь не использовать несколько операторов возврата, если функция не является тривиальной или оператор возврата до конца не является результатом ошибки. Преждевременное возвращение подчеркивает тот факт, что вы не можете продолжать. Для сложных функций, которые предназначены для разделения на несколько подфункций, я стараюсь кодировать их как операторы case (например, управляемые dict).

В некоторых плакатах упоминалась скорость работы. Скорость выполнения для меня второстепенна, поскольку, если вам нужна скорость выполнения, Python - не лучший язык для использования. Я использую Python, поскольку он важен для меня как эффективность кодирования (т.е. написание кода без ошибок).

Стивен Эллвуд
источник
1
Если пользователь собирается проголосовать против моего ответа, я был бы признателен за комментарий о том, почему они думают, что я был неправ.
Стивен Эллвуд
Я бы, вероятно, просто на строчку переделал бы по одной строке на оператор для удобства чтения. var n = 1 if (A > B) else -1 return A+n
Percebus
@percebus в некоторых случаях я бы согласился, если имя переменной может улучшить смысл. Например: 'code' move_x = 1 if my_x <оппонент_x else -1 # движение к оппоненту
Стивен Эллвуд
Кстати, я действительно поддержал ваш ответ. Если вы видите, мой ответ довольно похож
Percebus
1

Версия А проще, поэтому я бы ее и использовал.

И если вы включите все предупреждения компилятора в Java, вы получите предупреждение о второй версии, потому что это не имеет значения и увеличивает сложность кода.

Юрген Д.
источник
1

Я знаю, что вопрос помечен как python, но в нем упоминаются динамические языки, поэтому я подумал, что должен упомянуть, что в ruby ​​оператор if на самом деле имеет тип возврата, поэтому вы можете сделать что-то вроде

def foo
  rv = if (A > B)
         A+1
       else
         A-1
       end
  return rv 
end

Или потому, что он также имеет неявный возврат просто

def foo 
  if (A>B)
    A+1
  else 
    A-1
  end
end

который довольно хорошо решает проблему стиля отсутствия множественных возвратов.

Джейми Кук
источник