¿Es más eficiente usar if-return-return o if-else-return?

141

Supongamos que tengo una ifdeclaración con a return. Desde la perspectiva de la eficiencia, ¿debería usar

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

o

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

¿Debería preferir uno u otro al usar un lenguaje compilado (C) o uno con script (Python)?

Jorge Leitao
fuente
11
En un lenguaje compilado no necesita preocuparse demasiado por la eficiencia. El compilador lo resuelve. Debería escribir su código para poder leerlo. (Todavía debe preocuparse por la eficiencia de sus algoritmos, y el uso descuidado de los tipos, etc. afectará la eficiencia; simplemente no tiene que preocuparse demasiado por su estilo). Sin embargo, no sé sobre Python.
enms.
55
Confiar en su compilador para ordenar su código es un paso peligroso, y requiere un compilador infalible. ¡Mejor si sabes lo que quieres que haga tu código!
Andrew
1
Si lo que está haciendo está definido por la especificación, entonces no creo que haya ninguna razón para dudar del compilador. Habría sido escrito por personas mucho más inteligentes que tú, y es mucho más probable que hayas cometido un error que ellos.
será el
77
¿Cómo se puede cerrar esto para la opinión basada? Puede ser una opinión después de saber que no hay diferencia de rendimiento entre los dos. No lo hice, y estoy bastante seguro de que mucha gente tampoco lo hizo.
Jorge Leitao
1
Si bien la pregunta es bastante popular, no se puede responder con precisión sin un idioma específico en mente, o de lo contrario, responder para todos los idiomas sería demasiado largo para este formato.
Emile Bergeron

Respuestas:

195

Como la returndeclaración termina la ejecución de la función actual, las dos formas son equivalentes (aunque la segunda es posiblemente más legible que la primera).

La eficiencia de ambas formas es comparable, el código de máquina subyacente debe realizar un salto si la ifcondición es falsa de todos modos.

Tenga en cuenta que Python admite una sintaxis que le permite usar solo una returndeclaración en su caso:

return A+1 if A > B else A-1
Frédéric Hamidi
fuente
32
C también lo apoya. return (A>B)?A+1:A-1;Sin embargo, no hay absolutamente ninguna ganancia en el rendimiento al escribir el código de esta manera. Todo lo que hemos logrado es hacer que el código sea ofuscado, ilegible y, en algunos casos, más vulnerable a las promociones de tipo implícito.
Lundin
47
@Lundin ofuscado? ¿ilegible? Solo para aquellos que no conocen al operador ternario.
glglgl
66
@Lundin Seguir esta argumentación <es una mala práctica porque -1 < 1uproduce un resultado inesperado.
glglgl
3
@glglgl: No, porque la gente espera que el operador?: se comporte como si no, lo cual no es cierto. Si alguien escribiera código como -1 < 1u, lo cual dudo, fácilmente detectarían el error. Sin embargo, mucha gente escribiría alguna versión del código que publiqué. He visto tales errores con demasiada frecuencia en el código de producción para confiar en el operador?:. Además, como regla general, si el idioma le ofrece dos formas diferentes de hacer lo mismo, solo use una de ellas, no elija ninguna de las dos al azar según su estado de ánimo.
Lundin
66
@Lundin que es un argumento para tener cuidado con?: En C, pero parece decir que también se aplica a Python. ¿Puede señalar ejemplos en los que el uso del ternario en Python genere resultados inesperados?
lvc
33

De la guía de estilo de Chromium :

No use más después del regreso:

# Bad
if (foo)
  return 1
else
  return 2

# Good
if (foo)
  return 1
return 2

return 1 if foo else 2
skeller88
fuente
1
Gracias. +1. ¿Puedo preguntar por qué no usar otra cosa después de regresar?
Tim
1
if-else es funcionalmente equivalente, pero es detallado. Lo demás es innecesario.
skeller88
17
Me sorprendió porque lo primero parece más claro y, por lo tanto, mejor.
Tim
44
Podría hacer un caso razonable para cualquiera de los dos. Lo más importante en esta decisión IMO es ser coherente dentro de la base del código.
skeller88
2
probablemente encontrará en la mayoría de los casos que las if-else-returnramas casi nunca son iguales (si lo son, entonces debería refactorizar de todos modos; ya sea usando una switchconstrucción o para Python, enumerando un dict / usando un invocable / etc.). Por lo tanto, casi todos if-else-returnson casos de cláusulas de protección y siempre son comprobables (se burlan de la expresión probada) sin el else.
cowbert
5

En cuanto al estilo de codificación:

La mayoría de los estándares de codificación, sin importar el idioma, prohíben múltiples declaraciones de retorno de una sola función como mala práctica.

(Aunque personalmente diría que hay varios casos en los que las declaraciones de retorno múltiples tienen sentido: analizadores de protocolo de texto / datos, funciones con manejo extenso de errores, etc.)

El consenso de todos los estándares de codificación de la industria es que la expresión debe escribirse como:

int result;

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

En cuanto a la eficiencia:

El ejemplo anterior y los dos ejemplos en la pregunta son completamente equivalentes en términos de eficiencia. El código de máquina en todos estos casos tiene que comparar A> B, luego ramificarse con el cálculo A + 1 o A-1, luego almacenar el resultado de eso en un registro de CPU o en la pila.

EDITAR:

Fuentes:

  • MISRA-C: 2004 regla 14.7, que a su vez cita ...:
  • IEC 61508-3. Parte 3, tabla B.9.
  • IEC 61508-7. C.2.9.
Lundin
fuente
37
¿Estás seguro de que la religión de retorno único ha infectado la mayoría de los estándares de codificación? Eso sería aterrador.
Daniel Fischer
77
Yo diría que la regla no tiene sentido la mayor parte del tiempo. Tiendo a encontrar el código más legible y más fácil de seguir con devoluciones en los puntos apropiados. Pero solo soy yo. Sin embargo, pensé en los estándares de codificación por empresa / proyecto, no en cosas como MISRA donde las prescripciones idiotas de vez en cuando pueden tener algún mérito. Espero que la mayoría no haya aceptado la idea del punto de salida único.
Daniel Fischer
3
@DanielFischer: en el estándar de codificación C basado en MISRA que diseñé para mi empresa, tengo la regla "Una función solo tendrá un único punto de salida, al final de la función, a menos que un solo punto de salida haga el código menos legible ". Entonces es MISRA-C pero con una excepción a la regla. Si escribe una función de analizador avanzado que puede devolver, digamos 10 errores diferentes, el nivel de llaves anidadas hace que el código sea completamente ilegible; en tal caso, sería más sensato regresar de inmediato cuando se encuentre un error.
Lundin
66
Vea esta pregunta SO para una discusión y más enlaces a discusiones adicionales sobre el tema del punto de salida único. Además de que la regla del punto de salida único es anticuada y excesivamente "ingenierilista", Python promueve específicamente una vista de "plano es mejor que anidado" , y ponerlo return donde sea ​​que esté claro es la forma idiomática de hacerlo en Python.
John Y
1
@percebus Estoy completamente de acuerdo y la complejidad ciclomática es un buen argumento contra el retorno único. Y he estado molestando al comité de MISRA sobre esto varias veces, por ejemplo, mira esto . Al menos, la regla se rebajó a asesoría en MISRA-C: 2012.
Lundin
3

Con cualquier compilador sensato, no deberías observar ninguna diferencia; deben compilarse con un código de máquina idéntico ya que son equivalentes.

Oliver Charlesworth
fuente
2

Esta es una cuestión de estilo (o preferencia) ya que al intérprete no le importa. Personalmente, trataría de no hacer la declaración final de una función que devuelve un valor en un nivel de sangría que no sea la base de la función. El otro en el ejemplo 1 oculta, aunque solo sea ligeramente, dónde está el final de la función.

Por preferencia yo uso:

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

Como obedece tanto la buena convención de tener una sola declaración de retorno como la última declaración en la función (como ya se mencionó) y el buen paradigma de programación funcional de evitar resultados intermedios de estilo imperativo.

Para funciones más complejas, prefiero dividir la función en múltiples subfunciones para evitar retornos prematuros si es posible. De lo contrario, vuelvo a usar una variable de estilo imperativa llamada rval. Intento no usar múltiples declaraciones de retorno a menos que la función sea trivial o la declaración de retorno antes del final sea como resultado de un error. Regresar prematuramente resalta el hecho de que no puedes continuar. Para funciones complejas que están diseñadas para ramificarse en múltiples subfunciones, trato de codificarlas como declaraciones de casos (impulsadas por un dict, por ejemplo).

Algunos carteles han mencionado la velocidad de operación. La velocidad del tiempo de ejecución es secundaria para mí, ya que si necesita velocidad de ejecución, Python no es el mejor lenguaje para usar. Utilizo Python como la eficacia de la codificación (es decir, escribir código libre de errores) lo que me importa.

Stephen Ellwood
fuente
1
Si un usuario va a rechazar mi respuesta, agradecería un comentario sobre por qué pensaba que estaba equivocado.
Stephen Ellwood
Probablemente solo haría una línea antes para que sea 1 línea por declaración para fines de legibilidad. var n = 1 if (A > B) else -1 return A+n
percebus
@percebus en algunos casos, estaría de acuerdo si el nombre de la variable puede mejorar el significado. Por ejemplo: 'code' move_x = 1 if my_x <oponent_x else -1 # moverse hacia el oponente
Stephen Ellwood
Por cierto, en realidad voté por tu respuesta. Si ves mi respuesta es bastante similar
percebus
2

Yo personalmente evito los elsebloqueos cuando sea posible. Ver la campaña anti-si

Además, no cobran 'extra' por la línea, ya sabes: p

"Simple es mejor que complejo" y "La legibilidad es el rey"

delta = 1 if (A > B) else -1
return A + delta
Perbus
fuente
2
¿Por qué el voto negativo? Es una respuesta 'pitónica'. Es posible que no lo considere una respuesta preferida. Pero no es inválido. También estoy siguiendo el Principio de KISS en.wikipedia.org/wiki/KISS_principle
percebus
3
Elevé tu respuesta ya que para mí es muy fácil de leer. Personalmente, me parece ofensivo que alguien me rechace sin educarme sobre por qué mi respuesta es activamente negativa.
Stephen Ellwood el
1
No escuché antes sobre la campaña Anti-if, pero puedo entender por qué los ifs pueden ser peligrosos. Siempre trato de limitar la cantidad de código encerrado por una declaración if y trato de reescribir los árboles elif para usar dict. Sin embargo, esto se está volviendo un poco fuera de tema.
Stephen Ellwood el
1
@StephenEllwood Usar dicts para evitar diferencias es una muy mala idea en cuanto al rendimiento.
Bachsau
@Bachsau Probablemente tengas razón. Nunca tuve que preocuparme por el rendimiento ya que todos mis scripts se ejecutan en segundos. Para mí, la legibilidad generalmente supera el rendimiento. Como no soy programador a tiempo completo; son solo un medio para un fin.
Stephen Ellwood
1

La versión A es más simple y por eso la usaría.

Y si activa todas las advertencias del compilador en Java, recibirá una advertencia en la segunda versión porque es innecesaria y aumenta la complejidad del código.

juergen d
fuente
1

Sé que la pregunta está etiquetada como python, pero menciona lenguajes dinámicos, así que pensé que debería mencionar que en ruby, la instrucción if tiene un tipo de retorno para que pueda hacer algo como

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

O porque también tiene un retorno implícito simplemente

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

que evita el problema del estilo de no tener múltiples retornos bastante bien.

Jamie Cook
fuente