Ist es effizienter, if-return-return oder if-else-return zu verwenden?

141

Angenommen, ich habe eine ifErklärung mit a return. Aus Sicht der Effizienz sollte ich verwenden

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

oder

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

Sollte ich die eine oder andere bevorzugen, wenn ich eine kompilierte Sprache (C) oder eine Skriptsprache (Python) verwende?

Jorge Leitao
quelle
11
In einer kompilierten Sprache müssen Sie sich nicht viel um die Effizienz kümmern. Der Compiler regelt das. Sie sollten Ihren Code schreiben, damit Sie ihn lesen können. (Sie müssen sich immer noch um die Effizienz Ihrer Algorithmen sorgen, und die schlampige Verwendung von Typen usw. wirkt sich auf die Effizienz aus. Sie müssen sich nur nicht allzu viele Sorgen um Ihren Stil machen.) Ich weiß jedoch nichts über Python.
Ams
5
Sich auf Ihren Compiler zu verlassen, um Ihren Code zu sortieren, ist ein gefährlicher Schritt - und erfordert einen unfehlbaren Compiler. Besser, wenn Sie wissen, wie Sie Ihren Code ausführen sollen!
Andrew
1
Wenn das, was Sie tun, durch die Spezifikation definiert ist, dann glaube ich nicht, dass es einen Grund gibt, den Compiler anzuzweifeln. Es wird geschrieben worden sein, dass Menschen viel schlauer sind als Sie, und es ist weitaus wahrscheinlicher, dass Sie einen Fehler gemacht haben als sie.
wird
7
Wie kann dies für meinungsbasierte geschlossen werden? Es kann eine Meinung sein, nachdem Sie wissen, dass es keinen Leistungsunterschied zwischen den beiden gibt. Ich habe es nicht getan, und ich bin mir ziemlich sicher, dass viele Leute es auch nicht getan haben.
Jorge Leitao
1
Obwohl die Frage sehr beliebt ist, kann sie ohne eine bestimmte Sprache nicht genau beantwortet werden. Andernfalls wäre die Beantwortung für jede Sprache für dieses Format zu lang.
Emile Bergeron

Antworten:

195

Da die returnAnweisung die Ausführung der aktuellen Funktion beendet, sind die beiden Formen äquivalent (obwohl die zweite wohl besser lesbar ist als die erste).

Die Effizienz beider Formen ist vergleichbar, der zugrunde liegende Maschinencode muss einen Sprung ausführen, wenn die ifBedingung ohnehin falsch ist.

Beachten Sie, dass Python eine Syntax unterstützt, mit der Sie returnin Ihrem Fall nur eine Anweisung verwenden können:

return A+1 if A > B else A-1
Frédéric Hamidi
quelle
32
C unterstützt das auch. return (A>B)?A+1:A-1;Es gibt jedoch absolut keinen Leistungsgewinn, wenn Sie den Code so schreiben. Alles, was wir erreicht haben, ist, den Code verschleiert, unlesbar und in einigen Fällen anfälliger für implizite Typwerbung zu machen.
Lundin
47
@ Lundin verschleiert? unlesbar? Nur für diejenigen, die den ternären Operator nicht kennen.
glglgl
6
@Lundin Nach dieser Argumentation <ist es eine schlechte Praxis, weil sie zu -1 < 1ueinem unerwarteten Ergebnis führt.
glglgl
3
@glglgl: Nein, weil die Leute erwarten, dass sich der Operator ?: wie ein anderes verhält, was nicht stimmt. Wenn jemand Code wie schreiben würde -1 < 1u, was ich bezweifle, würde er den Fehler leicht erkennen. Sehr viele Leute würden jedoch eine Version des Codes schreiben, den ich gepostet habe. Ich habe solche Fehler viel zu oft im Produktionscode gesehen, um dem Operator ?: Zu vertrauen. Als Faustregel gilt: Wenn die Sprache Ihnen zwei verschiedene Möglichkeiten bietet, dasselbe zu tun, verwenden Sie nur eine davon. Wählen Sie je nach Stimmung keine der beiden zufällig aus.
Lundin
6
@Lundin, das ist ein Argument dafür, vorsichtig mit ?: In C zu sein, aber Sie scheinen zu sagen, dass es auch für Python gilt. Können Sie Beispiele nennen, bei denen die Verwendung des Ternärs in Python zu unerwarteten Ergebnissen führt?
lvc
33

Aus dem Chromium Style Guide:

Verwenden Sie nach der Rückkehr nichts anderes:

# Bad
if (foo)
  return 1
else
  return 2

# Good
if (foo)
  return 1
return 2

return 1 if foo else 2
skeller88
quelle
1
Vielen Dank. +1. Darf ich fragen, warum ich nach der Rückkehr nichts anderes benutze?
Tim
1
if-else ist funktional äquivalent, aber ausführlich. Das andere ist unnötig.
Skeller88
17
Ich war überrascht, weil der erste klarer und damit besser erscheint.
Tim
4
Sie könnten für beide einen vernünftigen Fall machen. Das Wichtigste bei dieser Entscheidung IMO ist, innerhalb der Codebasis konsistent zu sein.
Skeller88
2
Sie werden wahrscheinlich in den meisten Fällen feststellen, dass if-else-returnZweige fast nie gleich sind (wenn dies der Fall ist, sollten Sie trotzdem ein Refactoring durchführen; entweder mit einem switchKonstrukt oder für Python, mit der Aufzählung eines Diktats / mit einem aufrufbaren / etc.). Daher handelt es sich bei fast allen if-else-returnFällen um Schutzklauseln, und diese sind immer ohne den getesteten Ausdruck testbar (verspotten Sie den getesteten Ausdruck) else.
Cowbert
5

In Bezug auf den Codierungsstil:

Die meisten Codierungsstandards, unabhängig von der Sprache, verbieten mehrere Rückgabeanweisungen von einer einzelnen Funktion als schlechte Praxis.

(Obwohl ich persönlich sagen würde, dass es mehrere Fälle gibt, in denen mehrere return-Anweisungen sinnvoll sind: Text- / Datenprotokoll-Parser, Funktionen mit umfassender Fehlerbehandlung usw.)

Alle diese Kodierungsstandards der Branche sind sich einig, dass der Ausdruck wie folgt geschrieben werden sollte:

int result;

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

In Bezug auf die Effizienz:

Das obige Beispiel und die beiden Beispiele in der Frage sind alle hinsichtlich der Effizienz völlig gleichwertig . In all diesen Fällen muss der Maschinencode A> B vergleichen, dann entweder zur A + 1- oder zur A-1-Berechnung verzweigen und das Ergebnis in einem CPU-Register oder auf dem Stapel speichern.

EDIT:

Quellen:

  • MISRA-C: 2004 Regel 14.7, in der wiederum zitiert wird:
  • IEC 61508-3. Teil 3, Tabelle B.9.
  • IEC 61508-7. C.2.9.
Lundin
quelle
37
Sind Sie sicher, dass die Single-Return-Religion die meisten Kodierungsstandards infiziert hat? Das wäre erschreckend.
Daniel Fischer
7
Ich würde sagen, dass die Regel die meiste Zeit keinen Sinn ergibt. Ich finde Code besser lesbar und leichter zu verfolgen, wenn an geeigneten Stellen zurückgegeben wird. Aber das bin nur ich. Ich dachte jedoch an Kodierungsstandards pro Unternehmen / Projekt, nicht an Dinge wie MISRA, bei denen ansonsten idiotische Rezepte gelegentlich einen gewissen Wert haben könnten. Ich hoffe, die meisten haben sich nicht für die Idee eines einzelnen Ausstiegspunkts entschieden.
Daniel Fischer
3
@DanielFischer: In dem C-Codierungsstandard basierend auf MISRA, den ich für mein Unternehmen entworfen habe, habe ich die Regel "Eine Funktion darf am Ende der Funktion nur einen einzigen Austrittspunkt haben, es sei denn, ein einziger Austrittspunkt erstellt den Code weniger lesbar ". Es ist also MISRA-C, aber mit einer Ausnahme von der Regel. Wenn Sie eine erweiterte Parser-Funktion schreiben, die beispielsweise 10 verschiedene Fehler zurückgeben kann, macht die Ebene der verschachtelten Klammern den Code vollständig unlesbar. In einem solchen Fall wäre es sinnvoller, sofort zurückzukehren, wenn ein Fehler auftritt.
Lundin
6
In dieser SO-Frage finden Sie eine Diskussion und weitere Links zu weiteren Diskussionen zum Thema Single-Exit-Point. Abgesehen davon, dass die Single-Exit-Point-Regel altmodisch und übermäßig "technisch" ist, fördert Python speziell eine "Flat is Better as Nested" -Ansicht , und return wo immer es klar ist, ist es die idiomatische Methode, dies in Python zu tun.
John Y
1
@percebus Ich stimme voll und ganz zu und die zyklomatische Komplexität ist ein gutes Argument gegen eine einzelne Rendite. Und ich habe Stossen den MISRA Ausschuß über diese mehrmals, zum Beispiel das sehen . Zumindest wurde die Regel in MISRA-C: 2012 auf beratend herabgestuft.
Lundin
3

Bei jedem vernünftigen Compiler sollten Sie keinen Unterschied feststellen. Sie sollten zu identischem Maschinencode kompiliert werden, da sie gleichwertig sind.

Oliver Charlesworth
quelle
2

Dies ist eine Frage des Stils (oder der Präferenz), da es dem Dolmetscher egal ist. Persönlich würde ich versuchen, nicht die endgültige Aussage einer Funktion zu machen, die einen Wert auf einer anderen Einrückungsebene als der Funktionsbasis zurückgibt. Das else in Beispiel 1 verdeckt, wenn auch nur geringfügig, wo sich das Ende der Funktion befindet.

Bevorzugt benutze ich:

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

Da es sowohl der guten Konvention entspricht, eine einzige return-Anweisung als letzte Anweisung in der Funktion zu haben (wie bereits erwähnt), als auch dem guten funktionalen Programmierparadigma, imperative Zwischenergebnisse zu vermeiden.

Bei komplexeren Funktionen ziehe ich es vor, die Funktion in mehrere Unterfunktionen zu unterteilen, um vorzeitige Rückgaben nach Möglichkeit zu vermeiden. Ansonsten verwende ich wieder eine imperative Stilvariable namens rval. Ich versuche, nicht mehrere return-Anweisungen zu verwenden, es sei denn, die Funktion ist trivial oder die return-Anweisung vor dem Ende ist auf einen Fehler zurückzuführen. Eine vorzeitige Rückkehr unterstreicht die Tatsache, dass Sie nicht weitermachen können. Für komplexe Funktionen, die in mehrere Unterfunktionen verzweigen sollen, versuche ich, sie als case-Anweisungen zu codieren (z. B. gesteuert durch ein Diktat).

Einige Plakate haben die Betriebsgeschwindigkeit erwähnt. Die Laufzeitgeschwindigkeit ist für mich zweitrangig, da Python nicht die beste Sprache ist, wenn Sie Ausführungsgeschwindigkeit benötigen. Ich benutze Python als die Effizienz der Codierung (dh das Schreiben von fehlerfreiem Code), die für mich wichtig ist.

Stephen Ellwood
quelle
1
Wenn ein Benutzer meine Antwort ablehnen möchte, würde ich mich über einen Kommentar freuen, warum er dachte, ich hätte mich geirrt.
Stephen Ellwood
Ich würde wahrscheinlich nur eine Zeile vorher machen, um es aus Gründen der Lesbarkeit 1 Zeile pro Anweisung zu machen. var n = 1 if (A > B) else -1 return A+n
Percebus
@percebus in einigen Fällen würde ich zustimmen, wenn der Variablenname die Bedeutung verbessern kann. Zum Beispiel: 'Code' move_x = 1, wenn my_x <Gegner_x sonst -1 # in Richtung Gegner bewegen
Stephen Ellwood
Übrigens habe ich Ihre Antwort tatsächlich positiv bewertet. Wenn Sie sehen, ist meine Antwort ziemlich ähnlich
Percebus
2

Ich persönlich vermeide elseBlockaden, wenn möglich. Siehe die Anti-Wenn-Kampagne

Außerdem berechnen sie keine zusätzlichen Gebühren für die Leitung, wissen Sie: p

"Einfach ist besser als komplex" & "Lesbarkeit ist König"

delta = 1 if (A > B) else -1
return A + delta
Percebus
quelle
2
Warum die Abwahl? Ist eine "pythonische" Antwort. Sie könnten es nicht als bevorzugte Antwort betrachten. Ist aber nicht ungültig. Ich
folge
3
Ich habe Ihre Antwort positiv bewertet, da sie für mich lesbar und einfach ist. Ich persönlich finde es beleidigend, wenn mich jemand herabstimmt, ohne mich darüber zu informieren, warum meine Antwort aktiv negativ ist.
Stephen Ellwood
1
Ich habe vorher noch nichts über die Anti-Wenn-Kampagne gehört, kann aber verstehen, warum Wenns gefährlich sein können. Ich versuche immer, die Menge an Code zu begrenzen, die in einer if-Anweisung enthalten ist, und versuche, elif-Bäume neu zu schreiben, um dict zu verwenden. Dies gerät allerdings etwas vom Thema ab.
Stephen Ellwood
1
@StephenEllwood Die Verwendung von dicts zur Vermeidung von Unterschieden ist in Bezug auf die Leistung eine sehr schlechte Idee.
Bachsau
@Bachsau Du hast wahrscheinlich recht. Ich musste mich nie um die Leistung kümmern, da alle meine Skripte in Sekunden ausgeführt werden. Für mich übertrifft die Lesbarkeit normalerweise die Leistung. Da ich kein Vollzeitprogrammierer bin; Sie sind nur ein Mittel zum Zweck.
Stephen Ellwood
1

Version A ist einfacher und deshalb würde ich es verwenden.

Wenn Sie alle Compiler-Warnungen in Java aktivieren, erhalten Sie eine Warnung für die zweite Version, da diese nicht erforderlich ist und die Codekomplexität erhöht.

Jürgen d
quelle
1

Ich weiß, dass die Frage mit Python getaggt ist, aber sie erwähnt dynamische Sprachen, daher sollte ich erwähnen, dass die if-Anweisung in Ruby tatsächlich einen Rückgabetyp hat, damit Sie so etwas tun können

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

Oder weil es auch einfach implizite Rückkehr hat

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

Das umgeht das Stilproblem, nicht mehrere Retouren zu haben.

Jamie Cook
quelle