É mais eficiente usar if-return-return ou if-else-return?

141

Suponha que eu tenha uma ifafirmação com a return. Do ponto de vista da eficiência, devo usar

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

ou

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

Devo preferir um ou outro ao usar uma linguagem compilada (C) ou uma script (Python)?

Jorge Leitao
fonte
11
Em uma linguagem compilada, você não precisa se preocupar muito com eficiência. O compilador classifica isso. Você deve escrever seu código para poder lê-lo. (Você ainda precisa se preocupar com a eficiência de seus algoritmos, e o uso desleixado de tipos etc. afetará a eficiência - você não precisa se preocupar muito com seu estilo.) Porém, eu não conheço o Python.
AMS
5
Confiar no seu compilador para classificar o seu código é uma etapa perigosa - e requer um compilador infalível. Melhor se você souber o que quer que seu código faça!
Andrew
1
Se o que você está fazendo é definido pelas especificações, não acredito que exista qualquer razão para duvidar do compilador. Será escrito que as pessoas são muito mais espertas que você e é muito mais provável que você tenha cometido um erro do que elas.
será
7
Como isso pode ser fechado para se basear em opiniões? Pode ser uma opinião depois que você souber que não há diferença de desempenho entre os dois. Não, e tenho certeza de que muitas pessoas também não.
Jorge Leitao
1
Embora a pergunta seja bastante popular, ela não pode ser respondida com precisão sem um idioma específico em mente ou, caso contrário, a resposta para todos os idiomas seria muito longa para esse formato.
Emile Bergeron

Respostas:

195

Como a returninstrução encerra a execução da função atual, as duas formas são equivalentes (embora a segunda seja discutivelmente mais legível que a primeira).

A eficiência de ambos os formulários é comparável; o código de máquina subjacente deve executar um salto se a ifcondição for falsa de qualquer maneira.

Observe que o Python suporta uma sintaxe que permite usar apenas uma returninstrução no seu caso:

return A+1 if A > B else A-1
Frédéric Hamidi
fonte
32
C também suporta isso. return (A>B)?A+1:A-1;No entanto, não há absolutamente nenhum ganho no desempenho ao escrever o código dessa maneira. Tudo o que alcançamos é tornar o código ofuscado, ilegível e, em alguns casos, mais vulnerável a promoções implícitas de tipo.
Lundin
47
@Lundin ofuscado? ilegível? Somente para quem não conhece o operador ternário.
glglgl
6
@Lundin Seguindo essa argumentação, <é uma prática ruim porque -1 < 1uproduz um resultado inesperado.
glglgl
3
@glglgl: Não, porque as pessoas esperam que o operador?: se comporte como se-else, o que não é verdade. Se alguém escrevesse código como -1 < 1u, o que eu duvido, seria fácil identificar o bug. Muitas pessoas escreveriam alguma versão do código que eu publiquei. Vi esses bugs com muita frequência no código de produção para confiar no operador?:. Também como regra geral, se o idioma fornecer duas maneiras diferentes de fazer a mesma coisa, use apenas uma delas, não escolha aleatoriamente nenhuma das duas, dependendo do seu humor.
Lundin
6
@Lundin, que é um argumento para ter cuidado com?: Em C, mas você parece estar dizendo que se aplica ao Python também. Você pode apontar para alguns exemplos em que o uso do ternário em Python leva a resultados inesperados?
Lvc
33

Do guia de estilo do Chromium :

Não use mais depois do retorno:

# Bad
if (foo)
  return 1
else
  return 2

# Good
if (foo)
  return 1
return 2

return 1 if foo else 2
skeller88
fonte
1
Obrigado. +1. Posso perguntar por que não usar mais depois do retorno?
Tim
1
if-else é funcionalmente equivalente, mas é detalhado. O resto é desnecessário.
skeller88
17
Fiquei surpreso porque o primeiro parece mais claro e, portanto, melhor.
Tim
4
Você poderia fazer um argumento razoável para ambos. O mais importante nesta decisão da IMO é ser consistente dentro da base de código.
skeller88
2
você provavelmente encontrará na maioria dos casos que if-else-returnramificações quase nunca são iguais (se forem, então você deve refatorar de qualquer maneira; usando uma switchconstrução ou para Python, enumerando um dict / usando um callable / etc.). Portanto, quase todos if-else-returnsão casos de cláusulas de guarda e esses são sempre testáveis ​​(zombam da expressão testada) sem o else.
cowbert
5

Em relação ao estilo de codificação:

A maioria dos padrões de codificação, independentemente do idioma, proíbe várias declarações de retorno de uma única função como prática recomendada.

(Embora pessoalmente eu diria que há vários casos em que várias instruções de retorno fazem sentido: analisadores de protocolo de texto / dados, funções com amplo tratamento de erros, etc.)

O consenso de todos os padrões de codificação do setor é que a expressão deve ser escrita como:

int result;

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

Em relação à eficiência:

O exemplo acima e os dois exemplos na pergunta são todos equivalentes em termos de eficiência. Em todos esses casos, o código da máquina deve comparar A> B, ramificar para o cálculo A + 1 ou A-1 e armazenar o resultado disso em um registro da CPU ou na pilha.

EDIT:

Fontes:

  • MISRA-C: regra 14.7 de 2004, que por sua vez cita ...:
  • IEC 61508-3. Parte 3, tabela B.9.
  • IEC 61508-7. C.2.9
Lundin
fonte
37
Você tem certeza de que a religião de retorno único infectou a maioria dos padrões de codificação? Isso seria assustador.
22612 Daniel Fischer
7
Eu diria que a regra não faz sentido na maioria das vezes. Costumo encontrar código mais legível e mais fácil de seguir com retornos nos pontos apropriados. Mas sou só eu. No entanto, pensei nos padrões de codificação por empresa / projeto, não em coisas como MISRA, nas quais prescrições idiotas podem ocasionalmente ter algum mérito. Espero que a maioria não tenha aceitado a ideia do ponto de saída único.
22612 Daniel Fischer
3
@DanielFischer: No padrão de codificação C baseado no MISRA que eu projetei para minha empresa, tenho a regra "Uma função deve ter apenas um único ponto de saída, no final da função, a menos que um único ponto de saída faça o código" menos legível ". Portanto, é MISRA-C, mas com uma exceção à regra. Se você escrever uma função de analisador avançado que pode retornar, digamos 10 erros diferentes, o nível de chaves aninhadas torna o código completamente ilegível - nesse caso, seria mais sensato retornar imediatamente quando um erro fosse encontrado.
Lundin
6
Consulte esta pergunta do SO para uma discussão e links adicionais para discussões adicionais sobre a questão do ponto de saída único. Além de a regra do ponto de saída único ser antiquada e excessivamente "engenhosa", o Python promove especificamente uma visão "plana é melhor que aninhada" , e colocar return onde fica claro é a maneira idiomática de fazê-lo no Python.
John Y
1
@percebus Concordo plenamente e a complexidade ciclomática é um bom argumento contra o retorno único. E eu tenho cutucado o comitê MISRA sobre isso várias vezes, por exemplo, veja isso . Pelo menos a regra foi rebaixada para assessoria no MISRA-C: 2012.
Lundin
3

Com qualquer compilador sensato, você não deve observar nenhuma diferença; eles devem ser compilados em código de máquina idêntico, pois são equivalentes.

Oliver Charlesworth
fonte
2

Esta é uma questão de estilo (ou preferência), pois o intérprete não se importa. Pessoalmente, eu tentaria não fazer a declaração final de uma função que retorna um valor em um nível de recuo diferente da base da função. O resto do exemplo 1 obscurece, mesmo que ligeiramente, onde está o final da função.

Por preferência eu uso:

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

Como ele obedece à boa convenção de ter uma única declaração de retorno como a última declaração na função (como já mencionado) e ao bom paradigma de programação funcional de evitar resultados intermediários no estilo imperativo.

Para funções mais complexas, prefiro dividir a função em várias subfunções para evitar retornos prematuros, se possível. Caso contrário, volto a usar uma variável de estilo imperativa chamada rval. Tento não usar várias instruções de retorno, a menos que a função seja trivial ou a declaração de retorno antes do final seja resultado de um erro. O retorno prematuro destaca o fato de que você não pode continuar. Para funções complexas projetadas para ramificar-se em múltiplas subfunções, tento codificá-las como instruções de caso (orientadas por um ditado, por exemplo).

Alguns pôsteres mencionaram a velocidade de operação. A velocidade do tempo de execução é secundária para mim, pois se você precisar de velocidade de execução, o Python não é a melhor linguagem para usar. Eu uso o Python como a eficiência da codificação (isto é, escrever código sem erros) que é importante para mim.

Stephen Ellwood
fonte
1
Se um usuário fizer um voto negativo na minha resposta, eu apreciaria um comentário sobre o motivo pelo qual eles acharam que eu estava errado.
Stephen Ellwood
Eu provavelmente apenas uma linha antes para torná-lo 1 linha por instrução para fins de legibilidade. var n = 1 if (A > B) else -1 return A+n
percebus
@percebus em alguns casos, eu concordo que o nome da variável possa melhorar o significado. Por exemplo: 'código' move_x = 1 se my_x <opponent_x outra -1 # movimento no sentido oponente
Stephen Ellwood
BTW eu realmente votei na sua resposta. Se você ver a minha resposta é bastante semelhante
percebus
2

Pessoalmente, evito elsebloqueios quando possível. Veja a campanha anti-se

Além disso, eles não cobram 'extra' pela linha, você sabe: p

"Simples é melhor que complexo" e "Legibilidade é rei"

delta = 1 if (A > B) else -1
return A + delta
percebus
fonte
2
Por que o voto negativo? É uma resposta 'pitônica'. Você pode não considerar a resposta preferida. Mas não é inválido. Também estou seguindo o Princípio do KISS en.wikipedia.org/wiki/KISS_principle
percebus
3
Votei sua resposta uma vez que, para mim, é fácil e legível. Pessoalmente, acho ofensivo que alguém me dê um voto negativo sem me informar sobre por que minha resposta é ativamente negativa.
Stephen Ellwood
1
Não ouvi antes sobre a campanha Anti-if, mas pode entender por que os ifs podem ser perigosos. Eu sempre tento limitar a quantidade de código incluído por uma instrução if e tento reescrever as árvores elif para usar o dict. Isso está ficando um pouco fora de tópico.
Stephen Ellwood
1
@StephenEllwood Usar dicts para evitar diferenças é uma péssima idéia em termos de desempenho.
Bachsau 6/10/19
@Bachsau Você provavelmente está certo. Eu nunca tive que me preocupar com desempenho, pois todos os meus scripts são executados em segundos. Para mim, a legibilidade geralmente supera o desempenho. Desde que eu não sou um programador em tempo integral; eles são apenas um meio para um fim.
Stephen Ellwood
1

A versão A é mais simples e é por isso que eu a usaria.

E se você ativar todos os avisos do compilador em Java, receberá um aviso na segunda versão porque é desnecessário e aumenta a complexidade do código.

juergen d
fonte
1

Eu sei que a pergunta está marcada como python, mas menciona linguagens dinâmicas, então pensei em mencionar que, em ruby, a instrução if realmente tem um tipo de retorno para que você possa fazer algo como

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

Ou porque também possui retorno implícito simplesmente

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

que contorna a questão do estilo de não ter vários retornos muito bem.

Jamie Cook
fonte