Il est plus efficace d'utiliser if-return-return ou if-else-return?

141

Supposons que j'ai une ifdéclaration avec un return. Du point de vue de l'efficacité, devrais-je utiliser

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

ou

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

Dois-je préférer l'un ou l'autre lorsque j'utilise un langage compilé (C) ou un langage scripté (Python)?

Jorge Leitao
la source
11
Dans un langage compilé, vous n'avez pas besoin de vous soucier beaucoup de l'efficacité. Le compilateur trie cela. Vous devez écrire votre code pour pouvoir le lire. (Vous devez toujours vous soucier de l'efficacité de vos algorithmes, et une utilisation bâclée des types, etc. affectera l'efficacité - vous ne vous inquiétez tout simplement pas trop de votre style.) Cependant, je ne connais pas Python.
ams le
5
S'appuyer sur votre compilateur pour trier votre code est une étape dangereuse - et nécessite un compilateur infaillible. Mieux vaut si vous savez ce que vous voulez que votre code fasse!
Andrew le
1
Si ce que vous faites est défini par la spécification, alors je ne pense pas qu'il y ait de raison de douter du compilateur. Il aura été écrit avec des gens beaucoup plus intelligents que vous, et il est beaucoup plus probable que vous ayez fait une erreur qu'eux.
sera
7
Comment cela peut-il être fermé pour opinion? Cela peut être une opinion après que vous sachiez qu'il n'y a pas de différence de performance entre les deux. Je ne l'ai pas fait, et je suis presque sûr que beaucoup de gens ne l'ont pas fait non plus.
Jorge Leitao
1
Bien que la question soit assez populaire, elle ne peut pas être répondue avec précision sans une langue spécifique à l'esprit, ou sinon, répondre pour toutes les langues serait trop long pour ce format.
Emile Bergeron

Réponses:

195

Puisque l' returninstruction met fin à l'exécution de la fonction courante, les deux formes sont équivalentes (bien que la seconde soit sans doute plus lisible que la première).

L'efficacité des deux formes est comparable, le code machine sous-jacent doit effectuer un saut si la ifcondition est de toute façon fausse.

Notez que Python prend en charge une syntaxe qui vous permet d'utiliser une seule returninstruction dans votre cas:

return A+1 if A > B else A-1
Frédéric Hamidi
la source
32
C le prend également en charge. return (A>B)?A+1:A-1;Cependant, il n'y a absolument aucun gain de performances à écrire un code comme celui-ci. Tout ce que nous avons réalisé est de rendre le code obscurci, illisible et dans certains cas plus vulnérable aux promotions de type implicites.
Lundin
47
@Lundin obscurci? illisible? Uniquement pour ceux qui ne connaissent pas l'opérateur ternaire.
glglgl
6
@Lundin Suivre cette argumentation <est une mauvaise pratique car -1 < 1uproduit un résultat inattendu.
glglgl
3
@glglgl: Non, car les gens s'attendent à ce que l'opérateur?: se comporte comme if-else, ce qui n'est pas vrai. Si quelqu'un écrivait du code comme -1 < 1u, ce dont je doute, il repérerait facilement le bogue. Cependant, beaucoup de gens écriraient une version du code que j'ai posté. J'ai vu de tels bogues beaucoup trop souvent dans le code de production pour faire confiance à l'opérateur?:. En règle générale également, si la langue vous donne deux façons différentes de faire la même chose, n'en utilisez qu'une seule, ne choisissez pas l'une des deux au hasard en fonction de votre humeur.
Lundin le
6
@Lundin c'est un argument pour être prudent avec?: En C, mais vous semblez dire que cela s'applique également à Python. Pouvez-vous citer des exemples où l'utilisation du ternaire en Python conduit à des résultats inattendus?
lvc
33

Tiré du guide de style de Chromium :

Ne pas utiliser d'autre après le retour:

# Bad
if (foo)
  return 1
else
  return 2

# Good
if (foo)
  return 1
return 2

return 1 if foo else 2
skeller88
la source
1
Merci. +1. Puis-je demander pourquoi ne pas utiliser d'autre après le retour?
Tim
1
if-else est fonctionnellement équivalent, mais c'est verbeux. L'autre est inutile.
skeller88
17
J'ai été surpris car le premier semble plus clair et donc meilleur.
Tim
4
Vous pourriez faire un cas raisonnable pour l'un ou l'autre. La chose la plus importante dans cette décision IMO est d'être cohérent au sein de la base de code.
skeller88
2
vous trouverez probablement dans la majorité des cas que les if-else-returnbranches ne sont presque jamais égales (si elles le sont, alors vous devriez quand même refactoriser; soit en utilisant une switchconstruction ou pour Python, en énumérant un dict / en utilisant un appelable / etc.). Par conséquent, presque tous if-else-returnsont des cas de clauses de garde et ceux-ci sont toujours testables (simuler l'expression testée) sans l'extension else.
cowbert
5

Concernant le style de codage:

La plupart des normes de codage, quelle que soit la langue, interdisent les déclarations de retour multiples d'une seule fonction comme une mauvaise pratique.

(Bien que personnellement, je dirais qu'il existe plusieurs cas où plusieurs déclarations de retour ont du sens: analyseurs de protocole de texte / données, fonctions avec gestion des erreurs étendue, etc.)

Le consensus de toutes ces normes de codage de l'industrie est que l'expression doit être écrite comme suit:

int result;

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

Concernant l'efficacité:

L'exemple ci-dessus et les deux exemples de la question sont tous complètement équivalents en termes d'efficacité. Le code machine dans tous ces cas doit comparer A> B, puis passer au calcul A + 1 ou A-1, puis stocker le résultat de cela dans un registre CPU ou sur la pile.

ÉDITER :

Sources:

  • MISRA-C: règle 14.7 de 2004, qui à son tour cite ...:
  • CEI 61508-3. Partie 3, tableau B.9.
  • CEI 61508-7. C.2.9.
Lundin
la source
37
Êtes-vous sûr que la religion à retour unique a infecté la plupart des normes de codage? Ce serait effrayant.
Daniel Fischer
7
Je dirais que la règle n'a pas de sens la plupart du temps. J'ai tendance à trouver le code plus lisible et plus facile à suivre avec des retours aux points appropriés. Mais ce n'est que moi. Cependant, j'ai pensé aux normes de codage par entreprise / projet, pas à des choses comme MISRA où des prescriptions idiotes peuvent parfois avoir du mérite. J'espère que la plupart n'ont pas adhéré à l'idée de point de sortie unique.
Daniel Fischer
3
@DanielFischer: Dans le standard de codage C basé sur MISRA que j'ai conçu pour mon entreprise, j'ai la règle "Une fonction ne doit avoir qu'un seul point de sortie, à la fin de la fonction, sauf si un seul point de sortie fait le code moins lisible ". C'est donc MISRA-C mais avec une exception à la règle. Si vous écrivez une fonction d'analyse avancée qui peut renvoyer disons 10 erreurs différentes, le niveau d'accolades imbriquées rend le code complètement illisible - dans un tel cas, il serait plus judicieux de retourner immédiatement lorsqu'une erreur est rencontrée.
Lundin
6
Voir cette question SO pour une discussion et d'autres liens vers d'autres discussions sur la question du point de sortie unique. Outre que la règle du point de sortie unique est démodée et trop "d'ingénierie", Python promeut spécifiquement une vue "plate est meilleure que nichée" , et mettre return là où il se trouve être clair est la manière idiomatique de le faire en Python.
John Y
1
@percebus Je suis tout à fait d'accord et la complexité cyclomatique est un bon argument contre le retour unique. Et j'ai poussé le comité MISRA à plusieurs reprises à ce sujet, par exemple, voyez ceci . Au moins, la règle a été rétrogradée en conseil dans MISRA-C: 2012.
Lundin le
3

Avec n'importe quel compilateur sensible, vous ne devriez observer aucune différence; ils doivent être compilés avec un code machine identique car ils sont équivalents.

Oliver Charlesworth
la source
2

C'est une question de style (ou de préférence) puisque l'interprète s'en fiche. Personnellement, j'essaierais de ne pas faire la déclaration finale d'une fonction qui renvoie une valeur à un niveau d'indentation autre que la base de la fonction. Le else dans l'exemple 1 obscurcit, même légèrement, où se trouve la fin de la fonction.

De préférence j'utilise:

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

Comme il obéit à la fois à la bonne convention d'avoir une seule instruction return comme dernière instruction dans la fonction (comme déjà mentionné) et au bon paradigme de programmation fonctionnelle consistant à éviter les résultats intermédiaires de style impératif.

Pour les fonctions plus complexes, je préfère diviser la fonction en plusieurs sous-fonctions pour éviter les retours prématurés si possible. Sinon, je reviens à l'utilisation d'une variable de style impérative appelée rval. J'essaie de ne pas utiliser plusieurs déclarations de retour à moins que la fonction ne soit triviale ou que l'instruction de retour avant la fin soit le résultat d'une erreur. Revenir prématurément met en évidence le fait que vous ne pouvez pas continuer. Pour les fonctions complexes qui sont conçues pour se diviser en plusieurs sous-fonctions, j'essaie de les coder comme des instructions de cas (pilotées par un dict par exemple).

Certaines affiches ont mentionné la vitesse de fonctionnement. La vitesse d'exécution est secondaire pour moi car si vous avez besoin de vitesse d'exécution, Python n'est pas le meilleur langage à utiliser. J'utilise Python car c'est l'efficacité du codage (c'est-à-dire l'écriture de code sans erreur) qui m'importe.

Stephen Ellwood
la source
1
Si un utilisateur souhaite voter contre ma réponse, j'apprécierais un commentaire expliquant pourquoi il pense que je me trompe.
Stephen Ellwood
Je voudrais probablement juste une ligne avant pour en faire 1 ligne par déclaration à des fins de lisibilité. var n = 1 if (A > B) else -1 return A+n
percebus
@percebus dans certains cas, je serais d'accord si le nom de la variable peut améliorer la signification. Par exemple: 'code' move_x = 1 si mon_x <adversent_x else -1 # se déplace vers l'adversaire
Stephen Ellwood
BTW j'ai en fait voté pour votre réponse. Si vous voyez ma réponse est assez similaire
percebus
2

J'évite personnellement les elseblocages lorsque cela est possible. Voir le campagne anti-if

De plus, ils ne facturent pas de `` supplément '' pour la ligne, vous savez: p

"Simple est meilleur que complexe" et "La lisibilité est reine"

delta = 1 if (A > B) else -1
return A + delta
percebus
la source
2
Pourquoi voter contre? Est une réponse «pythonique». Vous pourriez ne pas considérer cela comme une réponse préférée. Mais n'est pas invalide. Je suis également le principe KISS en.wikipedia.org/wiki/KISS_principle
percebus
3
J'ai voté pour votre réponse car pour moi, elle se distingue par sa lisibilité et sa simplicité. Personnellement, je trouve offensant que quelqu'un me vote à la baisse sans m'expliquer pourquoi ma réponse est activement négative.
Stephen Ellwood
1
Je n'ai jamais entendu parler de la campagne anti-si, mais je peux comprendre pourquoi les si peuvent être dangereux. J'essaie toujours de limiter la quantité de code entourée par une instruction if et j'essaye de réécrire les arbres elif pour utiliser dict. Cela devient un peu hors sujet cependant.
Stephen Ellwood
1
@StephenEllwood Utiliser dicts pour éviter les différences est une très mauvaise idée en termes de performances.
Bachsau
@Bachsau Vous avez probablement raison. Je n'ai jamais eu à me soucier des performances car tous mes scripts s'exécutent en quelques secondes. Pour moi, la lisibilité l'emporte généralement sur les performances. Puisque je ne suis pas programmeur à plein temps; ils ne sont qu'un moyen pour atteindre une fin.
Stephen Ellwood
1

La version A est plus simple et c'est pourquoi je l'utiliserais.

Et si vous activez tous les avertissements du compilateur en Java, vous obtiendrez un avertissement sur la deuxième version, car il n'est pas nécessaire et augmente la complexité du code.

juergen d
la source
1

Je sais que la question est étiquetée python, mais elle mentionne des langages dynamiques, alors j'ai pensé que je devrais mentionner que dans ruby, l'instruction if a en fait un type de retour afin que vous puissiez faire quelque chose comme

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

Ou parce qu'il a aussi un retour implicite simplement

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

qui contourne le problème de style de ne pas avoir de retours multiples assez bien.

Jamie Cook
la source