Ich habe folgenden Code:
if (this->_car.getAbsoluteAngle() <= 30 || this->_car.getAbsoluteAngle() >= 330)
this->_car.edir = Car::EDirection::RIGHT;
else if (this->_car.getAbsoluteAngle() > 30 && this->_car.getAbsoluteAngle() <= 60)
this->_car.edir = Car::EDirection::UP_RIGHT;
else if (this->_car.getAbsoluteAngle() > 60 && this->_car.getAbsoluteAngle() <= 120)
this->_car.edir = Car::EDirection::UP;
else if (this->_car.getAbsoluteAngle() > 120 && this->_car.getAbsoluteAngle() <= 150)
this->_car.edir = Car::EDirection::UP_LEFT;
else if (this->_car.getAbsoluteAngle() > 150 && this->_car.getAbsoluteAngle() <= 210)
this->_car.edir = Car::EDirection::LEFT;
else if (this->_car.getAbsoluteAngle() > 210 && this->_car.getAbsoluteAngle() <= 240)
this->_car.edir = Car::EDirection::DOWN_LEFT;
else if (this->_car.getAbsoluteAngle() > 240 && this->_car.getAbsoluteAngle() <= 300)
this->_car.edir = Car::EDirection::DOWN;
else if (this->_car.getAbsoluteAngle() > 300 && this->_car.getAbsoluteAngle() <= 330)
this->_car.edir = Car::EDirection::DOWN_RIGHT;
Ich möchte die if
Kette vermeiden . es ist wirklich hässlich. Gibt es eine andere, möglicherweise sauberere Art, dies zu schreiben?
c++
if-statement
Oraekia
quelle
quelle
this->_car.getAbsoluteAngle()
einmal vor der ganzen Kaskade ficken .this
(this->
) ist nicht erforderlich und trägt nicht wirklich zur Lesbarkeit bei.>
Tests wäre der Code viel weniger hässlich . Sie werden nicht benötigt, da jeder von ihnen bereits in der vorherigenif
Anweisung (in entgegengesetzter Richtung) getestet wurde .else if
.Antworten:
So würde ich es machen. (Gemäß meinem vorherigen Kommentar).
quelle
q = a/b
undr = a%b
dannq * b + r
gleich sein müssena
. Daher ist es in C99 legal, dass ein Rest negativ ist. BorgLeader, mit dem Sie das Problem beheben können(((angle % 360) + 360) % 360) / 30
.GetDirectionForAngle
was ich als Ersatz für die if / else-Kaskade vorschlage, sie sind beide O (1) ...Sie können
map::lower_bound
die Obergrenze jedes Winkels in einer Karte verwenden und speichern.Arbeitsbeispiel unten:
quelle
table[angle%360/30]
Antworten gehen, weil es billig und verzweigungslos ist. Weitaus billiger als eine Baumsuchschleife, wenn diese zu asm kompiliert wird, das der Quelle ähnlich ist. (std::unordered_map
ist normalerweise eine Hash-Tabelle, aberstd::map
normalerweise ein rot-schwarzer Binärbaum. Die akzeptierte Antwort wird effektivangle%360 / 30
als perfekte Hash-Funktion für Winkel verwendet (nach dem Replizieren einiger Einträge, und Bijays Antwort vermeidet dies sogar mit einem Offset)).lower_bound
auf einem sortierten Array verwenden. Das wäre viel effizienter als einmap
.this->_car.getAbsoluteAngle()
mit einer tmp-Variable und Entfernen des redundanten Vergleichs aus den einzelnen OP-if()
Klauseln vereinfacht wird (Überprüfung auf etwas, das bereits übereinstimmt das vorhergehende if ()). Oder verwenden Sie den Vorschlag von @ wilx für sortierte Arrays.Erstellen Sie ein Array, dessen jedes Element einem Block von 30 Grad zugeordnet ist:
Dann können Sie das Array mit dem Winkel / 30 indizieren:
Keine Vergleiche oder Verzweigungen erforderlich.
Das Ergebnis weicht jedoch geringfügig vom Original ab. Werte an den Rändern, dh 30, 60, 120 usw., werden in die nächste Kategorie eingeordnet. Im ursprünglichen Code sind die gültigen Werte für
UP_RIGHT
beispielsweise 31 bis 60. Der obige Code weist 30 bis 59 zuUP_RIGHT
.Wir können dies umgehen, indem wir 1 vom Winkel abziehen:
Dies gibt uns jetzt
RIGHT
für 30,UP_RIGHT
für 60 usw.Im Fall von 0 wird der Ausdruck
(-1 % 360) / 30
. Dies ist gültig, weil-1 % 360 == -1
und-1 / 30 == 0
, so dass wir immer noch einen Index von 0 erhalten.Abschnitt 5.6 des C ++ - Standards bestätigt dieses Verhalten:
BEARBEITEN:
Es wurden viele Fragen zur Lesbarkeit und Wartbarkeit eines solchen Konstrukts aufgeworfen. Die Antwort von motoDrizzt ist ein gutes Beispiel für die Vereinfachung des ursprünglichen Konstrukts, das wartbarer und nicht ganz so "hässlich" ist.
Um seine Antwort zu erweitern, hier ein weiteres Beispiel, das den ternären Operator verwendet. Da jeder Fall im ursprünglichen Beitrag derselben Variablen zugewiesen wird, kann die Verwendung dieses Operators dazu beitragen, die Lesbarkeit weiter zu verbessern.
quelle
Dieser Code ist nicht hässlich, er ist einfach, praktisch, lesbar und leicht zu verstehen. Es wird auf seine eigene Weise isoliert, so dass sich niemand im Alltag damit auseinandersetzen muss. Und nur für den Fall, dass jemand dies überprüfen muss - möglicherweise, weil er Ihre Anwendung für ein Problem an einem anderen Ort debuggt -, ist es so einfach, dass er zwei Sekunden benötigt, um den Code und seine Funktionsweise zu verstehen.
Wenn ich ein solches Debugging durchführen würde, wäre ich froh, nicht fünf Minuten damit verbringen zu müssen, zu verstehen, was Ihre Funktion tut. In dieser Hinsicht scheitern alle anderen Funktionen vollständig, da sie eine einfache, fehlerfreie Routine zum Vergessen und Ändern in einem komplizierten Durcheinander ändern, das die Benutzer beim Debuggen gezwungen sind, gründlich zu analysieren und zu testen. Als Projektmanager würde ich mich sehr darüber aufregen, dass ein Entwickler eine einfache Aufgabe übernimmt und anstatt sie auf einfache, harmlose Weise zu implementieren, Zeit verschwendet, um sie auf eine zu komplizierte Weise zu implementieren. Denken Sie nur die ganze Zeit nach, die Sie damit verschwendet haben, darüber nachzudenken und dann zu SO zu fragen, und das alles nur, um die Wartung und Lesbarkeit der Sache zu verschlechtern.
Das heißt, es gibt einen häufigen Fehler in Ihrem Code, der ihn weniger lesbar macht, und ein paar Verbesserungen, die Sie ganz einfach vornehmen können:
Fügen Sie dies in eine Methode ein, weisen Sie dem Objekt den zurückgegebenen Wert zu, reduzieren Sie die Methode und vergessen Sie ihn für den Rest der Ewigkeit.
PS: Es gibt einen weiteren Fehler über der Schwelle von 330, aber ich weiß nicht, wie Sie ihn behandeln möchten, deshalb habe ich ihn überhaupt nicht behoben.
Späteres Update
Laut Kommentar können Sie sogar das andere loswerden, wenn überhaupt:
Ich habe es nicht getan, weil ich das Gefühl habe, dass ein bestimmter Punkt nur eine Frage der eigenen Vorlieben ist, und der Umfang meiner Antwort war (und ist), Ihrer Besorgnis über "Hässlichkeit des Codes" eine andere Perspektive zu geben. Wie gesagt, jemand hat in den Kommentaren darauf hingewiesen, und ich denke, es ist sinnvoll, es zu zeigen.
quelle
else if
,if
ist ausreichend.else if
. Ich finde es nützlich, einen Blick auf einen Codeblock werfen zu können und festzustellen, dass es sich eher um einen Entscheidungsbaum als um eine Gruppe nicht zusammenhängender Anweisungen handelt. Ja,else
oderbreak
sind für den Compiler nach a nicht erforderlichreturn
, aber sie sind nützlich für den Menschen, der einen Blick auf den Code wirft.elseif
/elsif
Schlüsselwort, oder Sie verwenden technisch gesehen einen Block mit einer Anweisung, der zufällig beginntif
, genau wie hier. Ein kurzes Beispiel dafür, woran Sie denken und woran ich denke: gist.github.com/IMSoP/90bc1e9e2c56d8314413d7347e76532aelse
was Sie dazu bringen, es ist ein schlechter Styleguide, der nichtelse if
als eindeutige Aussage erkannt wird . Ich würde immer geschweifte Klammern verwenden, aber ich würde niemals solchen Code schreiben, wie ich in meinem Kern gezeigt habe.Im Pseudocode:
So haben wir
0-60
,60-90
,90-150
, ... als die Kategorien. In jedem Quadranten mit 90 Grad hat ein Teil 60, ein Teil 30. Also, jetzt:Verwenden Sie den Index in einem Array, das die Aufzählungen in der entsprechenden Reihenfolge enthält.
quelle
quelle
Wenn Sie Ihren ersten ignorieren,
if
was ein Sonderfall ist, folgen die übrigen genau dem gleichen Muster: a min, max und Richtung; Pseudocode:Das Erstellen dieses echten C ++ könnte folgendermaßen aussehen:
Anstatt ein paar
if
s zu schreiben, sollten Sie jetzt einfach Ihre verschiedenen Möglichkeiten durchgehen:(
throw
Ing eine Ausnahme und nichtreturn
ingNONE
ist eine weitere Option).Was Sie dann nennen würden:
Diese Technik ist als datengesteuerte Programmierung bekannt . Abgesehen
if
davon, dass Sie ein paar s loswerden , können Sie auf einfache Weise weitere Richtungen hinzufügen (z. B. NNW) oder die Anzahl (links, rechts, oben, unten) reduzieren, ohne anderen Code zu überarbeiten.(Die Behandlung Ihres ersten Sonderfalls bleibt als "Übung für den Leser" übrig :-))
quelle
if(angle <= angleRange.max)
für die Verwendung von C ++ 11-Funktionen wie zenum class
. B. auf +1 reduzieren würde .Obwohl die vorgeschlagenen Varianten, die auf einer Nachschlagetabelle für
angle / 30
basieren, wahrscheinlich vorzuziehen sind, gibt es hier eine Alternative, die eine fest codierte binäre Suche verwendet, um die Anzahl der Vergleiche zu minimieren.quelle
Wenn Sie Doppelarbeit wirklich vermeiden möchten, können Sie dies als mathematische Formel ausdrücken.
Nehmen wir zunächst an, dass wir @ Geek's Enum verwenden
Jetzt können wir die Aufzählung mit ganzzahliger Mathematik berechnen (ohne dass Arrays erforderlich sind).
Wie @motoDrizzt hervorhebt, ist prägnanter Code nicht unbedingt lesbarer Code. Es hat den kleinen Vorteil, dass durch das Ausdrücken als Mathematik deutlich wird, dass einige Richtungen einen breiteren Bogen abdecken. Wenn Sie in diese Richtung gehen möchten, können Sie einige Asserts hinzufügen, um den Code besser zu verstehen.
Nachdem Sie die Asserts hinzugefügt haben, haben Sie eine Duplizierung hinzugefügt, aber die Duplizierung in Asserts ist nicht so schlimm. Wenn Sie eine inkonsistente Behauptung haben, werden Sie es früh genug herausfinden. Asserts können aus der Release-Version kompiliert werden, um die von Ihnen verteilte ausführbare Datei nicht aufzublähen. Dennoch ist dieser Ansatz wahrscheinlich am besten geeignet, wenn Sie den Code optimieren möchten, anstatt ihn nur weniger hässlich zu machen.
quelle
Ich bin zu spät zur Party, aber wir könnten Enum-Flags und Range Checks verwenden, um etwas Ordentliches zu tun.
die Überprüfung (Pseudocode) unter der Annahme, dass der Winkel absolut ist (zwischen 0 und 360):
quelle