Welche nützlichen alternativen Kontrollstrukturen kennen Sie? [geschlossen]


12

Ähnliche Frage wurde auf SO geschlossen.

Manchmal stellen wir beim Programmieren fest, dass eine bestimmte Kontrollstruktur für uns sehr nützlich ist, aber nicht direkt in unserer Programmiersprache verfügbar ist.

Welche alternativen Kontrollstrukturen sind Ihrer Meinung nach eine nützliche Möglichkeit, die Berechnung zu organisieren?

Ziel ist es, neue Denkansätze für die Strukturierung von Code zu entwickeln, um Chunking und Argumentation zu verbessern.

Sie können eine wünschenswerte Syntax / Semantik erstellen, die derzeit nicht verfügbar ist, oder eine weniger bekannte Kontrollstruktur für eine vorhandene Programmiersprache angeben.

Die Antworten sollten Anregungen für eine neue Programmiersprache oder die Verbesserung einer tatsächlichen Sprache geben.

Stellen Sie sich das als Brainstorming vor. Stellen Sie also etwas auf, das Ihrer Meinung nach eine verrückte Idee ist, aber in manchen Szenarien durchaus machbar ist.

Es geht um zwingende Programmierung.


1
Warum muss es sich bei dieser Frage um "Imperative Programmierung" handeln?
Fehlender Faktor

@missingfaktor: Weil es bei Kontrollstrukturen um zwingende Programmierung geht. Sicher, einige Probleme können mit funktionalem Ansatz oder auf andere Weise gelöst werden, aber andere Paradigmen stellen Kontrollstrukturen nicht in den Mittelpunkt der Entwicklung. Aber trotzdem kannst du kreativ sein.
Maniero

3
Nun, ich wollte Pattern Matching mit Guards teilen, da diese allgemeiner sind als Case-Anweisungen und If-Thens, aber wenn dies eine No-FP-Zone ist, denke ich, das ist nur mehr Pattern Matching für den Rest von uns!
CodexArcanum

@CodexArcanum: Nun, Pattern Matching ist ein Konstrukt, das imperativen Konstrukten sehr ähnlich ist. In der Tat ist Pattern Matching selbst eine Alternative zu zwingenden Kontrollstrukturen. Sei nicht schüchtern ;-)
Maniero

Bei all den Antworten freue ich mich, dass keiner der Vorschläge im wirklichen Leben existiert (und hoffentlich nie existiert). Sorry :)
Serg

Antworten:


14

OK, das ist eine lustige Frage.

Ich hätte auch gerne einen General elsefür eine Weile und für Schleifen, wenn die Bedingung beim ersten Test nicht erfüllt ist :

while (condition) {
    // process
}
else {
    // condition was never true
}

Dies vermeidet die umständliche Neuberechnung der Bedingung oder deren Speicherung in einer Variablen.


Python hat das.
Barry Brown

Hallo! Endlich eine skurrile Auswahl von Python, der ich zustimme! ;-)
Macneil

5
Nein. Die else-Klausel in Python for / while-Schleifen wird ausgeführt, wenn die Schleife normal endet. Dies bedeutet, dass die Schleife nicht durch eine break- oder return-Anweisung oder eine Ausnahme beendet wird. In for-Schleifen wird die else-Klausel ausgeführt, nachdem die Sequenz der Elemente, über die eine Schleife ausgeführt wird, erschöpft ist, und in while-Schleifen wird sie ausgeführt, nachdem die Schleifenbedingung zum ersten Mal auf False ausgewertet wurde.
Pillmuncher

4
Es ist eine Anforderung des hinzuzufügen while ... elseKonstrukt den Weg Macneil beschreibt es zu PHP. Ich denke, es ist eine großartige Idee, denn das "Hier sind die Ergebnisse / Es gab keine Ergebnisse" ist eine ziemlich verbreitete Redewendung in Web-Apps.
Dean Harding

1
@SnOrfus: Er möchte einen Codeblock (getrennt vom Schleifenblock), der NUR ausgeführt wird, wenn die while-Bedingung niemals erfüllt ist. Do-While führt den Codeblock der Schleife (nicht einen separaten Block) unabhängig von der while-Bedingung einmal aus.
Legion

10

Warum nicht ein paar Antworten zu einer zusammenfügen?

while (expr) {

    // Executed every iteration, unless first{} is present.
    // May be explicitly called rest{} if you like first{} to come first.

    // Blocks may return results, and consequently be used in expressions.
    return expr;

} first {

    // Executed only on the first iteration.

} pre {

    // Executed before every iteration.

} post {

    // Executed after every iteration.

} catch (oops) {

    // All blocks are implicitly try{}ed if followed by a catch{}.

} finally {

    // Executes after the block completes, regardless of exceptions.

} else {

    // Executed if the loop body or rest{} never executes.

} never {

    // Executes only when a client is present.

} drop (bad, worse), // Explicitly ignore certain exceptions.
  until (expr);      // Here, have a post-body condition, too.

Eine erweiterbare, verallgemeinerte Syntax für Flusssteuerungskonstrukte in einer imperativen Sprache wäre ziemlich nützlich und unterhaltsam. Bis das auftaucht, werde ich wohl nur Lisp oder so was benutzen.


5
Nicht nützlich, verwirrend. Nicht unterhaltsam, langsam.
Josh K

2
@Josh K: Ja, du hast recht. Was, dachtest du, würde ich nicht zustimmen? Diese Antwort beißt sich auf die Zunge, um nicht zu lachen.
Jon Purdy

Wenn es ironisch gemeint war, war es mit Sicherheit erfolgreich!
Josh K

11
+1 Hat mich zum Lachen gebracht. Benötigt eine "between" -Klausel, damit der Code zwischen den Iterationen ausgeführt werden kann.
Barry Brown

1
+1, aber es braucht auch eine seventh { ... }.
j_random_hacker

7

Kontrollstrukturen als Funktionen.

Ich will for, if, else, while, etc. Funktionen sein, keine besonderen Strukturen.

Ich möchte return, try/exceptund gotoDerivate von Fortsetzungen sein.

Dies hat natürlich weniger mit einer bestimmten Kontrollstruktur zu tun als vielmehr damit, wie Sie Kontrollstrukturen im Allgemeinen sehen, die Meta von Kontrollstrukturen.


2
Fortsetzungen sind ein Schwergewicht für eine Sprache, und es gibt gute Gründe, sie aus vielen Sprachen herauszulassen.
David Thornley

Ich bin nicht anderer Meinung. mit for, if, else, und whileals Funktionen macht mich daraus schließen , dass die Parameter für diese Funktionen träge, um das gleiche wie die ursprünglichen Konstrukte verhalten ausgeführt werden müssen. Es wäre schön, die Fähigkeit zu haben, eine faule Hinrichtung durchzuführen.
Dr. Wily's Apprentice

1
@ David: Nur weil sie da sind, heißt das nicht, dass du sie benutzen musst. Normale Programmierer können das verwenden, woran sie gewöhnt sind return, Ausnahmen usw. Jetzt hat der erfahrene Programmierer ein leistungsfähiges zusätzliches Werkzeug in seiner Toolbox, falls dies erforderlich ist. Außerdem sollten IMHO-Sprachen nicht "niedergeschlagen" werden. Sprachen sollten in der Lage sein, das Fachwissen zu erweitern und das CS-Wissen zu erweitern.
Dietbuddha

1
@dietbuddha: (Forts.) Ich sage nicht, dass Fortsetzungen schlecht sind, ich sage, dass sie Schwergewicht haben, und dass ihre Verwendung Auswirkungen auf die Sprache hat, so etwas wie Ausnahmen. Weiterhin ist es wahrscheinlich, dass Fortsetzungen Änderungen in der Verwendung der Sprache erzwingen, ähnlich wie bei Ausnahmen in C ++. Eine Sprache, die Fortsetzungen unterstützt, unterscheidet sich sowohl in guter als auch in schlechter Hinsicht von einer anderen Sprache.
David Thornley

1
@ Steve314 - longjmp ist keine Fortsetzung, obwohl es Ähnlichkeiten gibt. Fortsetzungen speichern den gesamten Zustand (alle Einheimischen, Globalen und den Stapel). longjmp speichert einen Stapel Zeiger so , wenn Sie den Umfang entkommen Vergangenheit , wo setjmp verwendet man einen segfault erhalten , da der Rahmen nicht mehr existiert. Ich glaube, es gibt auch einige Einschränkungen bei den Variablen, die gespeichert werden.
Dietbuddha

6

In dem verlinkten Artikel geht es definitiv um Donald Knuths N + 1/2-Loops . In C / C ++ / Java ausgedrückt:

for (;;) {
  get next element;
  if (at the end) break;
  process the element;
}

Dies ist nützlich, um Zeilen oder Zeichen aus einer Datei zu lesen, um zu prüfen, ob Sie EOF erreicht haben, und um sie dann zu verarbeiten. Ich bin es so gewohnt, das Muster zu sehen for(;;)..if(..)break;, dass es für mich idiomatisch ist. (Bevor ich den Artikel von Knuth gelesen hatte, der im Buch Literate Programming abgedruckt war , war dieser ein "wtf?".)

Knuth schlug die Schlüsselwörter vor loop/while/repeat:

loop:
  S;
while C:
  T;
repeat

Wo Sund Tsind Platzhalter für eine Reihe von null oder mehr Anweisungen und Cist eine boolesche Bedingung. Wenn es keine SAnweisung gäbe, wäre es eine while-Schleife, und wenn es keine TAnweisung gäbe, wäre es eine do-Schleife.

Dieses Konstrukt selbst könnte verallgemeinert werden, indem null oder mehr while CKlauseln zugelassen werden, was es perfekt macht, um Endlosschleifen und dann einige seltenere Bedingungen auszudrücken, die zwei Überprüfungen erfordern würden.

Im selben Artikel schlug Knuth einen Signalisierungsmechanismus vor, der eine lokale Version von Ausnahmen für das Werfen / Fangen von Ausnahmen ist (als Alternative zur Verwendung von goto).

Für mich? Ich wünsche mir, dass Java die Tail-Call-Optimierung unterstützt, damit ich bei Bedarf eine allgemeine Kontrollstruktur ausdrücken kann .


Update: Ich habe vergessen zu erwähnen, dass viele C / C ++ / Java-Programmierer dieses Problem umgehen, indem sie eine eingebettete Zuweisung verwenden, unter der Bedingung, dass while:

while ((c = getc(f)) != -1) {
   T;
}

Unter Verwendung der Begriffe aus Knuths Konstrukt ist dies zulässig, wenn Sund Ckann zu einem einzigen Ausdruck kombiniert werden. Einige Leute hassen es, die eingebettete Aufgabe oben zu sehen, während andere es hassen, breakdie for (;;)oben genannte Aufgabe zu sehen . Aber wenn Sund Ckann nicht kombiniert werden, beispielsweise wenn Smehrere Anweisungen hat, das for (;;)ist die einzige Alternative , ohne Code zu wiederholen. Die andere Alternative besteht darin, den SCode einfach zu duplizieren :

S;
while (C) {
  T;
  S;
}

Knuths loop/while/repeatAlternative scheint viel besser zu sein.


Diese Wiederholungsschleife sieht der Pascal- repeat-untilSchleife sehr ähnlich .
Mason Wheeler

@Mason: Es gibt zwei Stellen, an denen Sie Anweisungen und Ihre Bedingung einfügen können, und nicht nur eine.
Macneil

Oh, ich verstehe, was los ist. Ja, das ist interessant ...
Mason Wheeler

ANSI Basic hatte eine do while ... loop until- sowohl die Vorbedingung als auch die Nachbedingung sind optional, und ich erinnere mich (vage), beide in einer Schleife verwendet zu haben. Für deine Kondition gibt es die Ada exit when ...;. Der Hauptvorteil gegenüber if ... break;ist, dass Sie schreiben können, exit loopname when ...;um mehrere (aber nicht unbedingt alle) verschachtelte Schleifen gleichzeitig zu verlassen. Wahrscheinlich auch etwas sichtbarer als diese Pause.
Steve314

Lustige Sache. Ada hat genau diese Funktion.
John R. Strohm

6

Die BCPL-Sprache hatte einen valueofAusdruck , mit dem eine Folge von Anweisungen in einen einzelnen Ausdruck umgewandelt werden konnte:

foo(a, b, valueof {some series of statements; resultis v});

Wo some series of statementskann irgendetwas sein und das ganze valueofauswerten v.

Dies kann in Java nützlich sein, wenn Sie ein Argument für den Aufruf von a this()oder berechnen müssen super()(was voraussetzt, dass davor nichts passiert). Natürlich können Sie auch nur eine separate Methode schreiben, aber das kann schwierig sein, wenn Sie viele lokale Werte für den Kontext übergeben müssen.

Wenn Sie finalVariablen verwenden können, die benötigt werden, können Sie valueofin Java bereits anonyme innere Klassen verwenden:

foo(a, b, new Object(){String valueof(){
    String v ...; some series of statements; return v;}}.valueof());

1
GCC hat eine Erweiterung dafür - ({ statement1; statement2; ...; result-expr; }). Ähnliches habe ich auch anderswo gesehen, aber ich weiß nicht mehr, wo. Wahrscheinlich alle von BCPL kopiert.
Steve314

6
unless(condition) {
  // ...
}

macht dasselbe wie:

if(!condition) {
  // ...
}

repeat {
  // ...
} until(condition)

macht dasselbe wie:

do {
  // ...
} while(!condition)

Vielleicht möchten Sie nach Lisp ...
Duros

1
Oder Ruby, es hat eine ähnliche Syntax für unless.
Josh K

2
Sieht ganz Perlisch aus. Es gibt mehr als einen Weg, dies zu tun.

2
@ Steve314 Sie müssen einen falschen Klammerstil verwenden . ;-)
Orbling

1
@Orbling - überhaupt nicht. Alle anderen verwenden einen falschen Klammerstil.
Steve314

5

In einem anderen Sinne würde ich mir eine bessere Unterstützung für Iteratoren in Programmiersprachen wünschen. Insbesondere, wenn Sie zwei Sammlungen paarweise durchsuchen möchten :

for (String s, Integer i : stringsSet, integersSet) {
    // use the pair (s, i)
}

Einige dynamische Sprachen haben dies möglicherweise bereits oder können problemlos über Bibliotheken und Makros unterstützt werden, aber ich denke, dies liegt im Geiste Ihrer Frage.

Wenn die beiden Mengen nicht die gleiche Größe haben, kann es zu einer Ausnahme kommen, oder Sie können elsenach der Schleife eine Meldung anzeigen, dass ein Größenunterschied vorliegt.

Sie können dies natürlich verallgemeinern, indem Sie drei oder mehr Listen durchgehen.


Update: Auch nützlich wäre es, das kartesische Produkt zwischen iterablen zu machen:

for (String s, Integer i : stringsSet * integersSet) {
    // use the pair (s, i), each s with each i
}

Das wäre nichts anderes als verschachtelte Schleifen:

for (String s : stringsSet) {
    for (Integer i : integersSet) {
        // use the pair (s, i), each s with each i
    }
}

Ich bin ein wenig besorgt, dass zwischen den beiden Notationen, die ich hier angegeben habe, ein Unterschied in der Anzahl der Paare zwischen O (n) und O (n ^ 2) besteht, mit nur der Änderung eines einzelnen Zeichens.


2
In Python gibt es zip und zip_longest, die dies tun.
Pillmuncher

Cool, vielleicht unterschätze ich Python und sollte es nach vielen Jahren noch einmal genauer betrachten. Das erinnert mich daran, dass man manchmal auch das kartesische Produkt haben möchte, das für Schleifen verschachtelt ist.
Macneil

1
Beide Fälle in Scala: paste.pocoo.org/show/297429
missingfaktor

1
In Bezug auf das kartesische Produkt: Auch hier hat Python es. for a, b, c in itertools.product(iter1, iter2, iter3):gibt Ihnen das kartesische Produkt faul bewertet. Was ist das? Sie möchten auch Permutationen und Kombinationen eines bestimmten Iterators? itertools.permutations, itertools.combinations.
Aaronasterling

1
Haskell-Perspektive: Verwenden Sie "zipWith" für den ersten Fall und das Listenverständnis oder die Listenmonade für das zweite. Wie die Python / Scala-Beispiele, aber eleganter. :)
LennyProgrammers

5

Es gibt sogenannte "Dijkstra's Loop" (auch "Dijkstra's Guarded Loop" genannt). Es wurde in der Guarded Command Language (GCL) definiert . Informationen zu Syntax und Semantik finden Sie im obigen Wikipedia-Artikel in Abschnitt 6 Repetition: do .

Heutzutage kenne ich tatsächlich eine Programmiersprache, die dieses Kontrollmuster direkt unterstützt. Es ist Oberon-07 (PDF, 70 KB). Und es unterstützt "Dijkstra's Loop" in Form einer while-Anweisung. Schauen Sie sich Abschnitt 9.6 an. Während Aussagen im obigen PDF.

WHILE m > n DO m := m – n 
ELSIF n > m DO n := n – m 
END

PS Dies ist eine Kopie meiner SO-Antwort .


Es sieht so aus, als hätte der Model Checker Spin auch dieses Konstrukt mit identischer Semantik und im Grunde identischer Syntax.
j_random_hacker

4

Icon-ähnliche Ausdrücke mit integriertem Backtracking.

Python profitiert von vielen Vorteilen des Icon-Generators - und macht dies im Allgemeinen besser, IMO. Und im Prinzip war das Backtracking nur eine Art Ausnahmewurf, aber es war die Einfachheit der Ausdrücke, die grobe Entsprechung von ...

x = (a / b) else c;

Fehlerfälle wie Division durch Null zu behandeln.

Wo Icon durchgedreht ist - keine Vergleichsoperatoren, die Boolesche Werte zurückgeben. Die Vergleiche waren immer erfolgreich oder haben ein Zurückverfolgen ausgelöst, und es gab ein anderes semantisches Problem, an das ich mich jetzt verzweifelt zu erinnern versuche.

Ich habe immer gedacht, sie sollten einen ifAusdruck haben, der nichts anderes enthält if (condition, success-value), und zurückverfolgen, wenn die Bedingung falsch ist - und die seltsamen Vergleiche fallen lassen.

EDIT Ich erinnere mich - offensichtlich wirklich. Ein Vergleich mit zwei Argumenten ist entweder erfolgreich oder schlägt fehl - es wird kein neuer zurückzugebender Wert berechnet. Also, wenn es gelingt, was kehrt es zurück? Antwort - eines der Argumente. Aber wenn Sie schreiben a > b, welches ist das logische Argument, um zurückzukehren - aoder b? Und wenn du b < astattdessen schreibst ? Ich denke, es hat immer das richtige Argument geliefert, was genauso viel Sinn macht wie alles andere, aber es schien mir immer noch das falsche Argument zu sein.


4

Dies ist nur eine allgemeine Idee und Syntax:

if (cond)
   //do something
else (cond)
   //do something
also (cond)
   //do something
else
   //do something
end

AUCH Bedingung wird immer ausgewertet. ELSE funktioniert wie gewohnt.

Es funktioniert auch für den Fall. Wahrscheinlich ist es eine gute Möglichkeit, die break-Anweisung zu eliminieren:

case (exp)
   also (const)
      //do something
   else (const)
      //do something
   also (const)
      //do something
   else
      //do something
end

kann gelesen werden als:

switch (exp)
   case (const)
      //do something
   case (const)
      //do something
      break
   case (const)
      //do something
   default
      //do something
end

Ich weiß nicht, ob dies nützlich oder einfach zu lesen ist, aber es ist ein Beispiel.


3

Continuation Passing Style fällt mir ein. Dann möchten Sie natürlich auch Tail Call Optimization haben .


1
Kein großer Fan davon, und ich glaube nicht, dass es wirklich eine "Kontrollstruktur" ist, sondern eher ein Baustein in funktionalen Sprachen und im Programmierstil. Node.js scheint jedoch ziemlich süchtig danach zu sein.
Josh K

1
Natürlich ist es eine Kontrollstruktur. Es ist nicht nur eines, das mit einem Schlüsselwort wie 'if' oder 'for' geliefert wird, sondern als Muster zur Strukturierung des Kontrollflusses (also der Kontrollstruktur). Es wird sogar hinter den Kulissen von vielen Compilern verwendet. Und es ist auch nicht auf FLs beschränkt. Sie benötigen jedoch Funktionen als First-Class-Objekte.
Pillmuncher

1
Heutzutage erhalten Sie in C und C ++ eine Tail-Call-Optimierung, aber IMO geht der Punkt daneben. Der Punkt ist , dass es ist eine Optimierung. In Schema ist ein echter Tail Call offensichtlich. Insbesondere in C ++ kann vieles bedeuten, dass Ihr Tail Call kein Tail Call ist. Und ein Stapelüberlauf bedeutet, dass Ihre App kaputt ist. IMO sollte es so etwas wie eine goto return ...;Anweisung geben, die die Absicht hat, einen Tail-Call explizit auszuführen. Wenn der Compiler ihn also nicht iterativ ausführen kann, handelt es sich um einen Fehler.
Steve314

1
@Macneil - von dem ich mit Sicherheit weiß, dass die Tail-Call-Optimierung in GCC, Clang und Visual C ++ durchgeführt wird. GCC verfügt über komplexere Konvertierungen von Rekursion zu Iteration, die eine Reihe von Fällen verarbeiten können, bei denen es sich nicht um Endrekursionen handelt. Aber vieles kann schief gehen. Übergeben Sie in diesem Tail-Aufruf einen Zeiger auf eine lokale Variable, und der Stack-Frame kann nicht entfernt werden, da die Variable am Leben bleiben muss. In C ++ werden lokale Variablendestruktoren normalerweise ausgeführt, nachdem der Aufruf "tail" zurückgegeben wurde, was bedeutet, dass es sich überhaupt nicht um einen Tail-Aufruf handelt.
Steve314

1
@ Mike - das ist mein Punkt. Wenn Sie einen rekursiven Codierungsstil verwenden, ist Ihr Code ohne die Endaufrufoptimierung möglicherweise fehlerhaft, da ein Stapelüberlauf ein Fehler ist. In C ++ ist es eine Optimierung - für den Compiler ist es in Ordnung, Optimierungen vorzunehmen, damit Sie dies nicht tun müssen, aber Sie können sich nicht auf sie verlassen. Wenn Sie die Freiheit haben möchten, rekursiv zu schreiben, sich aber nicht um Probleme mit der Stapeltiefe kümmern müssen, ist die Tail-Call-Eliminierung keine Optimierung, sondern ein Korrektheitsproblem. Wenn Rekursion der beste Weg ist, um etwas zu codieren, sollten Sie in der Lage sein, dies zu tun - ohne sich um einen Stapelüberlauf sorgen zu müssen.
Steve314

3

Die nahtlose Thread-Verzweigung hat eine Syntax wie eine Funktion, wird jedoch in einem separaten Thread ausgeführt und kann nicht auf Daten zugreifen, die ursprünglich nicht an ihn übergeben wurden.

branch foo(data, to, be, processed){
    //code
    return [resulting, data]
}

Wenn ein Zweig aufgerufen wird, wird sofort ein Handle zurückgegeben.

handle=foo(here, is, some, data)

Mit dem Handle kann überprüft werden, ob die Aufgabe erledigt ist.

handle.finished() //True if the execution is complete

Wenn das Ergebnis angefordert wird, bevor die Ausführung abgeschlossen ist, wartet der Haupt-Thread einfach.

[result, storage]=handle.result()

Dies würde nicht die fortgeschritteneren Multithreading-Szenarien abdecken, sondern eine leicht zugängliche Möglichkeit bieten, mehrere Kerne zu nutzen.


Schauen Sie sich Cilk an, es ist eine sehr saubere und einfache Erweiterung von C: en.wikipedia.org/wiki/Cilk . Ich weiß nicht , ob es einen hat handle.finished()Test, aber spawnund syncalles , was Sie für 90% der parallelen Programmieraufgaben benötigen.
j_random_hacker

3
if (cond)
   //do something
else (cond)
   //do something
else (cond)
   //do something
first
   //do something
then
   //do something
else (cond)
   //do something
else
   //do something
end

Der Block FIRST und THEN wird ausgeführt, wenn eine der drei Bedingungen als wahr ausgewertet wird. Der ERSTE Block wird vor dem bedingten Block ausgeführt, und DANN wird ausgeführt, nachdem der bedingte Block ausgeführt wurde.

ELSE bedingte oder endgültige Schreibvorgänge nach der Anweisung FIRST und THEN sind von diesen Blöcken unabhängig.

Es kann lauten:

if (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   //do something
else
   //do something
end


function first()
   //do something
return
function then()
   //do something
return

Diese Funktionen sind nur ein Formular zum Lesen. Sie würden keinen Spielraum schaffen. Es ist eher ein Gosub / Return von Basic.

Nützlichkeit und Lesbarkeit als Diskussionsgegenstand.


2

Manchmal schreibe ich eine Schleife, die während der ersten Iteration etwas anderes machen muss. Beispiel: Anzeigen von <th> -Tags anstelle von <td> -Tags.

Ich behandle diese Situation mit einer Booleschen Flagge. Etwas wie das:

first = true

while (some_condition)
    if (first)
        do_something
        first = false
    else
        do_something_else

Es erscheint dumm, den Wert firstjeder Iteration zu überprüfen, wenn er die meiste Zeit falsch sein wird.

Ich hätte gerne eine Loop-Option, um bei der ersten Iteration einen anderen Loop-Body anzugeben. Es wäre keine separate Variable erforderlich. Der kompilierte Code würde auch keinen benötigen, da der generierte Code zwei Körper haben würde, einen für die erste Iteration und einen für den Rest.


Die SO-Frage hat eine ähnliche Idee, jedoch mit einem "dann" -Operator, der für Werte verwendet werden soll. Auf diese Weise haben Sie wahrscheinlich keinen doppelten Code. ZBprint(out, first "<th>" then "<td>")
Macneil

1
Besser ist es, entweder die Iteration +1 zu starten.
Josh K

1
In der Regel wird eine Schleife mit Zwischenhandhabung ausgeführt, z. B. Auflisten von Elementen mit einem Kommatrennzeichen. Streng genommen ist das if (!first) gimme-a-comma ();aber fast dasselbe. Ein Einwand, den ich allerdings haben würde - wenn Sie es richtig zusammenfassen, erhalten Sie Dinge wie die Python-String-Join-Methode -, obwohl Sie oft das Grundmuster benötigen, muss die zugrunde liegende Schleife nicht so oft umgeschrieben werden.
Steve314

Wie Josh sagt, ist es natürlich gültig, den ersten Gegenstand aus der Schleife zu ziehen. Im Komma-Trennzeichen-Fall bedeutet dies doppelten Code, dies kann jedoch ein doppelter Funktionsaufruf sein. Persönlich bevorzuge ich die Ineffizienz der if (!first), aber Mikrooptimierer können Einwände gegen die Verzweigungsvorhersage erheben.
Steve314

1
@Barry Brown: Das Abrollen der ersten Iteration der Schleife kann schneller sein oder auch nicht als das Überprüfen eines Booleschen Flags. Ein anständiger Verzweigungsprädiktor wird im schlimmsten Fall die ersten zwei Iterationen falsch vorhersagen. Ich würde es vorziehen if (!first), einen optimierenden Compiler zu verwenden und entscheiden zu lassen, ob der Schleifenkörper klein genug ist, um sich abzuwickeln und loszuwerden, firstist ein Nettogewinn.
j_random_hacker

1

[kopiert von meiner eigenen Antwort auf stackoverflow]


ignoring - Um Ausnahmen zu ignorieren, die in einem bestimmten Codeblock auftreten.

try {
  foo()
} catch {
  case ex: SomeException => /* ignore */
  case ex: SomeOtherException => /* ignore */
}

Mit einem ignorierenden Kontrollkonstrukt könnten Sie es übersichtlicher und lesbarer schreiben als:

ignoring(classOf[SomeException], classOf[SomeOtherException]) {
  foo()
}

[Scala stellt dieses (und viele andere Ausnahmebehandlungs-Steuerungskonstrukte) in seiner Standardbibliothek im Paket util.control zur Verfügung. ]


4
Ausnahmen sollten nicht ignoriert werden.
Josh K

1
Abgesehen davon, dass eine Ausnahme im Kontext möglicherweise kein Fehler ist.
Steve314

1
@Josh, @Steve: Es gibt Fälle, in denen Sie die Ausnahmen ignorieren möchten. In diesem Thread finden Sie einige solcher Fälle.
Fehlender Faktor

Eine Ausnahme ist eine Warnung, dass etwas nicht stimmt. Ein leerer try..catchBlock ist eine Sache; es zwingt Sie, den Fehler zu erkennen und ihn absichtlich zu ignorieren; Das Werfen von Codestücken unter einem globalen Ignorieren kann zu Problemen führen, wenn diese Ausnahmen ausgelöst werden, und zu schlechten Programmiergewohnheiten.
Josh K

@Josh - Habe gerade zwei Kommentare gelöscht, weil ich nicht klar gedacht habe - aber die Hoffnungen für diesen sind groß. Wenn diese Aussage global ist, stimme ich wahrscheinlich zu - aber sie sieht für mich wie eine Blockstruktur aus. IOW ist wie ein tryBlock, nur werden die Ausnahmen aufgelistet, die er abfängt und ignoriert, anstatt später. Dies könnte sogar einen Vorteil für die Lesbarkeit bedeuten - z. B. den Leser wissen lassen, dass eine fehlende Datei kein Fehler ist, noch bevor er den offenen Aufruf liest.
Steve314

1

Anstatt von:

switch(myEnum) {
  case MyEnum.Val1: do1(); ...
  case MyEnum.Val2: do2(); ...
....

Mach es mit Python oder jetzt auch mit C #:

action = val2func[myEnum]
action()

Scala hat viele neue Funktionen.

Schließlich können Sprachen wie Clojure erweitert werden, um die zusätzliche Funktionalität bereitzustellen.


1
C kann das auch. Und Pascal. Eine wahrscheinlich all diese alten 70er / 80er / 90er Sprachen. Ein Array von Funktionen wird zu einem Array von Funktionszeigern, aber das ist keine große Sache.
Einfacher

Steve314 - Korrektur - Dies ist ein Beispiel für ein Wörterbuch, das einer Funktion einen beliebigen Wert zuordnet. Hier werden die Dinge etwas sauberer als die meisten Dinge aus den 70er / 80er / 90er Jahren.
Job

1

Ich habe zwei Ideen.

Oft stelle ich fest, dass ich mich in catchBlöcken wiederhole . Dies kann etwas durch Extrahieren von Methoden geholfen werden, aber dies kann zu unnötigem Durcheinander führen, wenn die Methoden sehr kurz sind oder anderweitig keine Methode wert sind. Es wäre also schön, catchBlöcke zu verschachteln :

try {
    // Save something
} catch (Exception e) {
    // Something we do for all Exceptions
    catch (ProcessingException e) {
        // Something we do for all Processing exceptions
        catch (DBExcpetion e) {
            // DBExceptions are a subclass of ProcessingException
        }
        catch (BusinessRuleException e) {
            // BusinessRuleExceptions are also a subclass of ProcessingException
        }
    }
    // Something we do after specific sub class Exceptions
 }

In der Web-Programmierung tue ich oft so etwas (dies ist kein wirkliches Beispiel, also analysiere keine fiktiven Fälle):

Account a = getSavedAccount();
if (a == null) {
    a = getAccountFromSessionId();
}
if (a == null) {
    a = getAccountFromCookieId();
}
if (a == null) {
    a = createNewAccount();
}

In Javascript (naja, ECMAScript und vielleicht anderen, mit denen ich nicht vertraut bin) ||kann es helfen , da jeder Wert als Bedingung bewertet werden kann.

var a = getAFromLocation1() || getAFromLocation2() || default;

Ich mag es wirklich, wie das aussieht, und ich wünschte, mehr Sprachen, insbesondere einige auf der Serverseite, hätten Unterstützung dafür. (PHP kann alles als Bedingung auswerten, konvertiert aber den gesamten Bedingungsausdruck in einen Booleschen Ausdruck, anstatt den Wert beizubehalten. Ich weiß nicht, wie Python oder Ruby aussehen.) Nach drei oder mehr Fällen könnte es unhandlich werden, aber wenn Sie mehr haben In mehr als drei Fällen liegt möglicherweise auch ein fehlerhaftes Software-Design vor.


Python macht so etwas wie Ihr || Auswertung, aber wenn es schließlich seine bedingten Ausdruck bekam x if c else ySyntax, ein wichtiger Grund , das war geschehen , weil eine Menge von Ausdrücken dieser Semantik für die Verwendung ||und &&war auf subtile Weise Buggy. IIRC Ein häufiger Fall war ein Wert (z. B. Null), der für die zu behandelnde Anwendung gültig war falseund daher verworfen wurde, sodass stattdessen ein ungültiger Fallback verwendet wurde. Allerdings - siehe meine Antwort WRT der Icon-Programmiersprache, die Ausdrücke wie haben kann get1() else get2() else default.
Steve314

1

Der verallgemeinerte Schalter hat oben gesagt:

 switch(x){
  predicate1:
     dosomething();
  predicate2:
     dosomethingelse();
 }

In Haskell:

  switch' :: a -> [(a -> Bool, b)] -> b
  switch' a [] = undefined
  switch' a (f,b):xs = if f a
                     then b
                      else switch' a xs

0

In C # möchte ich einfache switch () { ... }, aber mit solchen Ausdrücken erweiterbare verwenden:

switch (value)
{
  // string-based operators:
  case begins "Maria": // to catch Maria Carey
    break;
  case ends "Washington": // to catch George Washington
    break;
  case like "ph": // to catch Phil, Phillip, Sophie
    break;
  case between "Aaron" and "April": // to catch all names between
    break;

  // use non-static variables in case expression:
  case Dao.GetDefaultBabyName():
    break;

  // continuable cases without breaking
  case "John":
    bonus = 25;
  case "Peter":
    salary = 500;
    break;

  // jumps between cases
  case "Aleron":
    // do something
    break;
  case "Bella":
    // do something
    jump "Aleron";
    break;

}

Und so weiter. Das gleiche mit Zahlen oder anderen Typen (dh Träger IComparable, IConvertible...)

Dies könnte meinen Code lakonischer und lesbarer machen.


Das Durchfallen von Fällen ist ein bekanntes Übel, und ich bin im Allgemeinen in Ordnung, ohne es. Und Sprünge sind zu GOTOish für jede vernünftige Programmierung. Aber Ausdrücke und Nicht-Statik in dem Fall zu haben, wäre eine schöne Ergänzung.
CodexArcanum

0

Es ist eine lustige Frage, wie @Macneil sagte.

Meine ungewöhnliche Lieblingskontrollstruktur, die ich (demütiger Husten) entdeckt habe, ist die differentielle Ausführung .

Es hat bestimmte Verwendungen. Die überwältigende Verwendung besteht für mich in der Programmierung von Benutzeroberflächen, was ein Beispiel für das allgemeinere Problem ist, redundante Daten in Übereinstimmung zu halten. Auf der einen Seite gibt es Anwendungsdaten und auf der anderen Seite gibt es Benutzeroberflächen-Steuerelemente, die in Übereinstimmung gehalten werden müssen. Das klingt nach "Bindung", aber es steckt noch viel mehr dahinter.

Normalerweise implementiere ich es durch Makros in C oder C ++. In C # muss ich es durch handexpandierende Anweisungen tun. Das ist ein Schmerz, aber es funktioniert.

Einmal habe ich es in Form von Lisp-Makros implementiert und dann war es sehr sauber. Es erforderte keine Sorgfalt seitens des Programmierers. Ich hätte das Gleiche in jeder anderen strukturierten Sprache tun können, wenn ich mir die Mühe gemacht hätte, einen vollständigen Parser zu schreiben und dann alles Richtige zu generieren. Das ist ein großes Projekt, und ich habe es nicht getan.


0

Bei "traditionellen" Kontrollstrukturen geht fores darum, den Arbeiter zu kontrollieren und ihn den korrupten Ideologien der herrschenden kapitalistischen Elite zu unterwerfen. Deshalb verwende ich ph0rstattdessen alternative Kontrollstrukturen wie . Es ist wie for, aber radikaler: Sie werden es nicht fangen ph0r, einen Anzug und eine Krawatte zu tragen, die etwas Corporate BS ausstoßen. ph0rhält es echt, Mann.

Kämpfe gegen die Macht!


0

Einfachstes forLoop-

for(100)
{
    //Will run for 100 times
}


for(i)
{
    //Will run for i times while i must be a positive integer
}


for(i as a)
{
    //Will run for i times while i must be a positive integer
    //and a is the incremental loop variable starting from 0 and 
    //scoped within the loop
}


for(i as a=2)
{
    //Will run for i times while i must be a positive integer
    //and a is the incremental loop variable starting from 2 and 
    //scoped within the loop
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.