Programmierung ohne if-Anweisungen? [geschlossen]

8

Ich erinnere mich an einige Zeit (Jahre, wahrscheinlich) vor ich las auf Stackoverflow über den Charme der Programmierung mit so wenigen Wenn-Tests wie möglich. Diese Frage ist etwas relevant, aber ich denke, dass der Stress auf der Verwendung vieler kleiner Funktionen lag Diese Rückgabewerte wurden durch Tests in Abhängigkeit von dem Parameter ermittelt, den sie erhalten haben. Ein sehr einfaches Beispiel wäre dies:

%Vor%

mit isSmall() wie folgt aussehen:

%Vor%

anstatt nur das zu tun:

%Vor%

(Logisch ist dieser Code nur ein Beispielcode. Er ist nicht Teil eines Programms, das ich mache.)

Der Grund dafür war, glaube ich, weil es schöner aussieht und einen Programmierer weniger anfällig für logische Fehler macht. Wenn diese Kodierungskonvention korrekt angewendet wird, würden Sie praktisch keine if-Tests an irgendeiner Stelle sehen, außer in Funktionen, deren einziger Zweck darin besteht, diesen Test durchzuführen .

Nun, meine Frage ist: Gibt es Dokumentation über diese Konvention? Gibt es irgendwo wo man wilde Auseinandersetzungen zwischen Unterstützern und Gegnern dieses Stils sehen kann? Ich habe versucht, nach dem Stackoverflow-Post zu suchen, der mich dazu gebracht hat, aber ich kann ihn nicht mehr finden.

Zum Schluss hoffe ich, dass diese Frage nicht abgelehnt wird, weil ich nicht nach einer Lösung für ein Problem frage. Ich hoffe einfach, mehr über diesen Kodierungsstil zu erfahren und vielleicht die Qualität aller Kodierungen, die ich in Zukunft machen werde, zu erhöhen.

    
Lee White 19.04.2013, 07:57
quelle

4 Antworten

6

Dieses ganze "if" vs "no if" Ding lässt mich an das Ausdrucksproblem denken 1 . Grundsätzlich ist es eine Beobachtung, dass Programmierung mit if-Anweisungen oder ohne if-Anweisungen eine Frage der Kapselung und Erweiterbarkeit ist und dass es manchmal besser ist, if-Anweisungen zu verwenden, und manchmal ist es besser, dynamisches Dispatching mit Methoden / Funktionen zu verwenden Zeiger.

Wenn wir etwas modellieren wollen, müssen wir uns um zwei Achsen kümmern:

  1. Die verschiedenen Fälle (oder Typen) der Eingaben, mit denen wir uns befassen müssen.
  2. Die verschiedenen Operationen, die wir über diese Eingaben ausführen möchten.

Eine Möglichkeit, diese Art von Dingen zu implementieren, ist if statements / pattern matching / das Besuchermuster:

%Vor%

Die andere Möglichkeit ist die Objektorientierung:

%Vor%

Es ist nicht schwer zu erkennen, dass die erste Version, die if-Anweisungen verwendet, das Hinzufügen neuer Operationen zu unserem Datentyp erleichtert: Erstellen Sie einfach eine neue Funktion und führen Sie eine Fallanalyse durch. Auf der anderen Seite ist es schwierig, unserem Datentyp neue Fälle hinzuzufügen, da dies bedeutet, dass wir das Programm zurückgehen und alle verzweigenden Anweisungen modifizieren müssten.

Die zweite Version ist genau das Gegenteil. Es ist sehr einfach, dem Datentyp neue Fälle hinzuzufügen: Erstellen Sie einfach eine neue "Klasse" und sagen Sie, was Sie für jede der Methoden tun müssen, die wir implementieren müssen. Es ist jedoch schwierig, der Schnittstelle neue Operationen hinzuzufügen, da dies bedeutet, dass für alle alten Klassen, die die Schnittstelle implementiert haben, eine neue Methode hinzugefügt wird.

Es gibt viele verschiedene Ansätze, mit denen Sprachen versuchen, das Ausdrucksproblem zu lösen und es einfach machen, einem Modell sowohl neue Fälle als auch neue Operationen hinzuzufügen. Es gibt jedoch Vor-und Nachteile dieser Lösungen 3 so im Allgemeinen denke ich, es ist eine gute Faustregel, zwischen OO und if-Anweisungen je nachdem, welche Achse Sie es einfacher zu erweitern möchten, Zeug zu wählen.

Wie auch immer, zurück zu Ihrer Frage gibt es einige Dinge, auf die ich hinweisen möchte:

Der erste ist, dass ich glaube, dass das OO-Mantra, alle if-Anweisungen loszuwerden und sie durch Methoden-Dispatching zu ersetzen, mehr damit zu tun hat, wie die meisten OO-Sprachen keine typsicheren algebraischen Datentypen haben mit "if statesmsnts" ist schlecht für die Kapselung. Da die einzige Methode zur Typsicherheit darin besteht, Methodenaufrufe zu verwenden, sollten Sie Programme mit if-Anweisungen in Programme konvertieren, die das Besuchermuster 4 oder schlechter: Konvertieren Sie Programme, die das Besuchermuster verwenden sollen, mit einfachen Methoden in Programme, wodurch die Erweiterbarkeit in die falsche Richtung erleichtert wird.

Die zweite Sache ist, dass ich kein großer Fan davon bin, Dinge in Funktionen zu bremsen, nur weil Sie es können. Insbesondere finde ich diesen Stil, wo alle Funktionen nur 5 Zeilen haben und viele andere Funktionen aufrufen, ziemlich schwer zu lesen.

Schließlich denke ich, dass dein Beispiel nicht wirklich von Aussagen befreit wird. Was Sie tun, ist im Wesentlichen eine Funktion von Integers zu einem neuen Datentyp (mit zwei Fällen, einer für Big und einer für Small) und dann müssen Sie immer noch if-Anweisungen verwenden, wenn Sie mit dem Datentyp arbeiten:

%Vor%

Wenn wir zurück zum Ausdrucksproblem gehen, liegt der Vorteil unserer toSize / isSmall-Funktion darin, dass wir die Logik wählen, in welchem ​​Fall unsere Zahl an einem einzigen Ort passt und dass unsere Funktionen nur für den Fall danach funktionieren Das. Dies bedeutet jedoch nicht, dass wir die Anweisungen aus unserem Code entfernt haben! Wenn die ToSize eine Factory-Funktion ist und wir Big und Small-Klassen haben, die eine Schnittstelle teilen, dann ja, wir werden if-Anweisungen aus unserem Code entfernen. Wenn out isSmall jedoch nur einen booleschen Wert oder eine Enumeration zurückgibt, dann werden genauso viele if-Anweisungen wie zuvor existieren. (und Sie sollten auswählen, welche Implementierung verwendet werden soll, je nachdem, ob Sie neue Methoden oder neue Fälle - sagen wir Medium - in Zukunft hinzufügen möchten).

1 - Der Name des Problems kommt von dem Problem, wo Sie einen "Ausdruck" -Datentyp haben (Zahlen, Variablen, Addition / Multiplikation von Teilausdrücken, usw.) und Dinge wie Evaluierungsfunktionen und andere Dinge implementieren wollen.

>

2 - Oder Mustervergleich über Algebraische Datentypen, wenn Sie typsicherer sein wollen ...

3 - Sie müssen beispielsweise alle Multimethoden auf der "obersten Ebene" definieren, auf der der "Dispatcher" sie sehen kann. Dies ist eine Einschränkung im Vergleich zum allgemeinen Fall, da if-Anweisungen (und lambdas) tief in anderen Code eingebettet werden können.

4 - Im Wesentlichen eine "Kirchencodierung" eines algebraischen Datentyps

    
hugomg 21.04.2013, 07:11
quelle
1

Ist es wirklich eine Konvention? Sollte man nur minimale If-Konstrukte töten, nur weil es Frustration darüber geben könnte?

Ok, wenn Aussagen dazu neigen außer Kontrolle zu geraten, besonders wenn viele Spezialfälle im Laufe der Zeit hinzugefügt wurden. Eine Verzweigung nach der anderen wird hinzugefügt, und am Ende ist niemand in der Lage zu verstehen, was alles funktioniert, ohne stundenlang Zeit und ein paar Tassen Kaffee in diesen großen Spaghetti-Code zu investieren.

Aber ist es wirklich eine gute Idee, alles in separate Funktionen zu stellen? Der Code sollte wiederverwendbar sein. Code sollte lesbar sein. Aber ein Funktionsaufruf erzeugt nur die Notwendigkeit, in der Quelldatei nachzuschlagen. Wenn alle ifs auf diese Weise weggeräumt werden, springen Sie ständig in der Quelldatei herum. Unterstützt dies die Lesbarkeit?

Oder betrachten Sie eine if-Anweisung, die nirgendwo wiederverwendet wird. Sollte es wirklich in eine separate Funktion gehen, nur aus Gründen der Konvention? Hier ist auch etwas Overhead involviert. Performance-Probleme könnten auch in diesem Zusammenhang relevant sein.

Was ich sagen möchte: Die folgenden Kodierungskonventionen sind gut. Stil ist wichtig. Aber es gibt Ausnahmen. Versuchen Sie einfach, guten Code zu schreiben, der in Ihr Projekt passt, und behalten Sie die Zukunft im Auge. Codierungskonventionen sind letztlich nur Richtlinien, die uns helfen, guten Code zu produzieren, ohne etwas auf uns anzuwenden.

    
Lord Wolfchild 19.04.2013 08:31
quelle
1

Ich habe noch nie von einer solchen Konvektion gehört. Ich sehe sowieso nicht, wie es funktioniert. Sicherlich ist der einzige Punkt, an dem ein iIsSmall ist, später darauf zu verzweigen (möglicherweise in Kombination mit anderen Werten)?

Was ich gehört habe, ist ein Argument, um Variablen wie iIsSmall überhaupt zu vermeiden . iIsSmall speichert nur das Ergebnis eines von Ihnen durchgeführten Tests, so dass Sie später dieses Ergebnis verwenden können, um eine Entscheidung zu treffen. Warum also nicht einfach den Wert von i an dem Punkt testen, an dem Sie die Entscheidung treffen müssen? d.h. statt:

%Vor%

schreibe einfach:

%Vor%

Auf diese Weise können Sie am Verzweigungspunkt feststellen, wo Sie sich gerade befinden, weil es genau dort ist. Das ist in diesem Beispiel sowieso nicht schwer, aber wenn der Test kompliziert war, werden Sie wahrscheinlich nicht in der Lage sein, das Ganze im Variablennamen zu codieren.

Es ist auch sicherer. Es besteht keine Gefahr, dass der Name iIsSmall irreführend ist, weil Sie den Code so geändert haben, dass er etwas anderes testet, oder weil i tatsächlich geändert wurde, nachdem Sie isSmall aufgerufen haben, sodass es nicht mehr unbedingt klein sein muss habe gerade einen dummen Variablennamen usw. gewählt.

Offensichtlich funktioniert das nicht immer. Wenn der Test isSmall teuer ist und Sie mehrmals auf das Ergebnis verzweigen müssen, möchten Sie ihn nicht oft ausführen. Möglicherweise möchten Sie den Code dieses Aufrufs auch nicht mehrmals duplizieren, es sei denn, es ist trivial. Oder Sie möchten das Flag zurückgeben, das von einem Aufrufer verwendet wird, der nicht von i weiß (obwohl Sie dann isSmall(i) zurückgeben könnten, anstatt es in einer Variablen zu speichern und dann die Variable zurückzugeben).

Übrigens, die separate Funktion speichert in Ihrem Beispiel nichts. Sie können (i < 10) in eine Zuweisung zu einer bool -Variable genauso einfach einbinden wie in einer return-Anweisung in einer bool -Funktion. d. h. du könntest genauso einfach bool isSmall = i < 10; schreiben - das vermeidet die if-Anweisung, nicht die separate Funktion. Code der Form if (test) { x = true; } else { x = false; } oder if (test) { return true; } else { return false; } ist immer dumm; Verwenden Sie einfach x = test oder return test .

    
Ben 20.04.2013 02:07
quelle
1

Dies könnte ein guter Anfang sein. Ich habe das von diese Frage bekommen.

    
Louie 19.04.2013 08:12
quelle