Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Verschachtelte Schleifen beenden
HenryHux - Sa 02.10.10 16:06
Titel: Verschachtelte Schleifen beenden
Hi,
immoment habe ich ein Programm mit viele repeat-Schleifen und darin vielen verschiedenen, verschachtelten If-Abfragen.
Die Schleifen beende ich mit Variablen, also wenn der entsprechende If-Fall eingetreten ist, wird der Wert auf die Bedingung von Until gesetzt.
Jetzt wollte ich mal Fragen, geht das nicht einfacher?
Ich habe von break und goto gelesen.
break bringt mir aber nichts, weils nur eine Schleife beendet. (richtig verstanden?)
Womit kann ich eine ganze Schleife, mit mehreren kleineren beenden?
Lg
Henry
glotzer - Sa 02.10.10 16:08
mit goto und Labels
mal in der helife nachlesen :p
bole - Sa 02.10.10 16:17
Ich würde Goto unbedingt vermeiden! Die macht den Code extrem unleserlich und unwartbar. Break ist zwar schon besser aber meines Erachtens auch ein verkapptes GOTO.
Ich denke du solltest Deine Schleifenkonstruktion überarbeiten, durch die Wahl der richtigen Schleife kann man dies meist stark vereinfachen.
HenryHux - Sa 02.10.10 16:34
Jo wollte ich auch immer, kannte es aus anderen Sprachen und fand es nie gut es zu benutzen.
Aber jetzt sind in einem Programm so verschatelte Schleifen, mit vielen If Abfragen, die nicht parallel eintreten können.
Dh wenn die erste If Anweisung zutrifft laufen die anderen Anweisungen auch weiter durch.
Einerseits sehr langsam und zweitens mir irgendwie nicht sicher.
Und break könnte ich bei ja auch nur so setzen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| If ... then begin anweisung1; anweisung2; break; end; |
Und das bringt mir ja auch keine Verbesserung.. Deswegen goto.
Danke aber tortzdem =)
jaenicke - Sa 02.10.10 16:44
Wie wäre es denn mit else?
HenryHux - Sa 02.10.10 16:50
Letztendlich hat das in repeat-Schleifen ja auch keinen Unterschied..
jaenicke - Sa 02.10.10 16:53
Ich habe das darauf bezogen:
HenryHux hat folgendes geschrieben : |
Dh wenn die erste If Anweisung zutrifft laufen die anderen Anweisungen auch weiter durch.
Einerseits sehr langsam und zweitens mir irgendwie nicht sicher. |
Das hörte sich so an:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| if a then begin xy; end; if b then begin xy; end; |
Deshalb meinte ich das else, damit die zweite if-Abfrage nicht mehr ausgeführt wird.
Ja, ansonsten bleibt nur die Schleifenbedingungen gut zu setzen. ;-)
Und damit es nicht zu unübersichtlich wird, ist es natürlich sinnvoll manches in einzelne Methoden auszulagern.
HenryHux - Sa 02.10.10 17:00
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| if a then begin xy; end; if b then begin xy; end; |
Ist doch von der Geschwindigkeit und generell das gleiche wie
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| if a then begin xy; end; else if b then begin xy; end; |
Oder irre ich mich?
Marc. - Sa 02.10.10 17:09
Schau Dir doch einfach dazu den Debugger Code an. Breakpoint setzen (F5), Anwendung starten und mit Strg+Alt+C in das CPU-Fenster wechseln.
Hier sieht man, dass im Falle eines
else am Ende der Ausführung der ersten Bedingung ein JMP hinter die Zweite gesetzt wird. ;) Somit ist dies u.U. schneller.
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| Unit1.pas.33: if (s = 'a') then 00466568 8B45FC mov eax,[ebp-$04] 0046656B BAB8654600 mov edx,$004665b8 00466570 E8BBF6F9FF call @UStrEqual 00466575 7503 jnz $0046657a Unit1.pas.35: nop 00466577 90 nop 00466578 EB10 jmp $0046658a Unit1.pas.38: if (s = 'b') then 0046657A 8B45FC mov eax,[ebp-$04] 0046657D BAC8654600 mov edx,$004665c8 00466582 E8A9F6F9FF call @UStrEqual 00466587 7501 jnz $0046658a Unit1.pas.40: nop 00466589 90 nop Unit1.pas.42: end; 0046658A 33C0 xor eax,eax |
HenryHux - Sa 02.10.10 17:22
Ok.
Immoment habe ich einfach alle If-Anweisungen ganz normal verschachtelt und hintereinander geschrieben.
Sollte ich generell lieber mit else arbeiten?
platzwart - Sa 02.10.10 17:36
Kannst du mal den realen Code posten? Dann könnte man sicherlich besser helfen...
HenryHux - Sa 02.10.10 17:45
In der einen procedure wo meine Frage drüber war sind mehr als 2k Zeilen drin.
Denke nicht gerade die beste Idee das hier zu posten =)
Aber meine Frage hat sich schon geklärt.
Habe die "parallelen" Ifs mit else if gemacht.
So wie ich das verstanden hab, werden sobald davon eine richtig ist, die danachstehenden Ifs alle nicht mehr abgefragt.
Tilman - Sa 02.10.10 17:47
Eventuell könnte auch case das richtige für dich sein. Btw: wenn eine Prozedur > als 2000 Zeilen Code hat, dann läuft irgendwas falsch ;) du solltest dann Codeteile in weitere prozeduren auslagern, und schauen ob du Code "wiederverwenden" kannst, also vielleicht in vielen Zeilen immer fast das selbe machst.
HenryHux - Sa 02.10.10 17:56
Case ist If, quasi mit oder, richtig? :D
Das wäre auch nicht gut, denn es sind viele verschiedene Aktionen in verschiedenen Situationen.
Das einzige was mir noch zu schaffen macht, ist die until Bedingung, die ich dann bei jedem If auf 0 setzen muss.
Ansonsten ist der Code schon fast maximal in anderen Funktionen bzw. Prodzeduren ausgelagert.
jaenicke - Sa 02.10.10 18:06
Wenn du else nutzt, dann wird der Teil im else nur ausgeführt, wenn die erste if-Abfrage nicht wahr war. Also genau was du willst.
Und zu den 2000 Zeilen wurde ja schon etwas gesagt:
Das ist dein größtes Problem... Also 200-300 Zeilen ist schon viel, aber das kann schon mal passieren. Aber 2000, in Worten zweitausend, Zeilen?!?
Da solltest du dringend deine Codestruktur korrigieren, dann lösen sich die anderen Probleme vermutlich von selbst...
HenryHux - Sa 02.10.10 18:18
Is mein erstes wirklich größeres Programm, das ich schreibe.
Nun schon fast seit einem Monat.
Aber dachte, das wäre völlig normal mal in einer Procedure ein bisschen mehr stehen zu aheb :D
Aber ich wüsste ernsthaft nicht, wie ich den Code noch viel mehr auslagern sollte.
jaenicke - Sa 02.10.10 18:24
HenryHux hat folgendes geschrieben : |
Aber ich wüsste ernsthaft nicht, wie ich den Code noch viel mehr auslagern sollte. |
Wenn du eine einigermaßen neue Version von Delphi hast, macht Delphi das automatisch für dich. Nicht unbedingt schön, ich mache es meistens lieber manuell, aber es funktioniert.
Dafür markierst du den Teil, der ausgelagert werden soll, und gehst unter Refactoring auf Methode extrahieren.
Es gibt jedenfalls bestimmt Möglichkeiten zur Auslagerung. Du musst immer schauen was du logisch in kleine Stücke teilen kannst und das dann in eigene Prozeduren packen. So kannst du nebenbei solche Codeteile dann auch viel besser wiederverwenden.
HenryHux - Sa 02.10.10 18:37
Ok, danke für den Tipp, wusst ich nicht, werds gleich mal ausprobieren.
Habe jetzt mal alles mit GoTo bearbeitet.
Sind nur 2 Labels, weiß aber nicht ob das die beste Idee war.
Natürlich springt er sofort woanders hin, aber was mich stört, ist dass Labels anscheinend nur Lokal zu deklarieren sind.
D.h wenn ich per GoTo in einer andere Procedur springen will, geht das nicht direkt.
Habe jetzt am Schluss der Prozedur, wo ich rausspringen will, eine praktisch unerfüllbar If-Anweisung gesetzt, wo ich das Label rein positioniert habe.
Vor dem letzten Ende kommt dann nur noch mal der Verweis auf die andere Prozedur, dürfte also funktionieren.
Was haltet ihr davon?
elundril - Sa 02.10.10 18:41
HenryHux hat folgendes geschrieben : |
Ok, danke für den Tipp, wusst ich nicht, werds gleich mal ausprobieren.
Habe jetzt mal alles mit GoTo bearbeitet.
Sind nur 2 Labels, weiß aber nicht ob das die beste Idee war.
Natürlich springt er sofort woanders hin, aber was mich stört, ist dass Labels anscheinend nur Lokal zu deklarieren sind.
D.h wenn ich per GoTo in einer andere Procedur springen will, geht das nicht direkt.
Habe jetzt am Schluss der Prozedur, wo ich rausspringen will, eine praktisch unerfüllbar If-Anweisung gesetzt, wo ich das Label rein positioniert habe.
Vor dem letzten Ende kommt dann nur noch mal der Verweis auf die andere Prozedur, dürfte also funktionieren.
Was haltet ihr davon? |
ja, keine schlechte idee wenn man Programmierer in der Herzinfakt- oder Selbstmordtot treiben will. Für ernstes Programmieren sind aber GoTos n absolutes No-Go. ;)
lg elundril
jaenicke - Sa 02.10.10 18:42
HenryHux hat folgendes geschrieben : |
Natürlich springt er sofort woanders hin, aber was mich stört, ist dass Labels anscheinend nur Lokal zu deklarieren sind.
D.h wenn ich per GoTo in einer andere Procedur springen will, geht das nicht direkt. |
Das wäre ja der Super-GAU. Schlimm genug, dass es goto überhaupt gibt. Die Anwendungsfälle, in denen das wirklich sinnvoll ist kann man quasi an einer Hand abzählen. Dein Programm gehört zu 99,999% sicher nicht dazu...
HenryHux hat folgendes geschrieben : |
Was haltet ihr davon? |
Nichts, mach es besser richtig statt solche Notkonstrukte zu basteln...
Marc. - Sa 02.10.10 18:42
HenryHux hat folgendes geschrieben : |
Ok, danke für den Tipp, wusst ich nicht, werds gleich mal ausprobieren.
Habe jetzt mal alles mit GoTo bearbeitet.
Sind nur 2 Labels, weiß aber nicht ob das die beste Idee war. |
2000 Zeilen und GOTO - dafür gehörst Du gesteinigt. :mrgreen:
Du wirst sicher merken, dass Du Dich nach einer Weile überhaupt nicht mehr in deinem Quellcode zurechtfinden wirst.
Wie schaut denn diese Routine in etwa aus? Ich bin mir sicher, da ist noch sehr viel Optimierungsbedarf. ;)
Ansonsten einfach
jaenickes Vorschlag folgen. Quelltext auslagern und gegebenfalls via Klasse zusammenfassen. ;)
HenryHux - Sa 02.10.10 18:46
Ok, danke für die warnenden Hinweise.
Werde mich jetzt erstmal selbst geißeln und mir dann was anderes einfallen lassen :D
Mit dem Auslagern klappt soweit alles ganz gut, danke ist jetzt viel übersichtlicher geworden.
Lg
Henry
Martok - Sa 02.10.10 19:03
jaenicke hat folgendes geschrieben : |
Das wäre ja der Super-GAU. Schlimm genug, dass es goto überhaupt gibt. Die Anwendungsfälle, in denen das wirklich sinnvoll ist kann man quasi an einer Hand abzählen. Dein Programm gehört zu 99,999% sicher nicht dazu... |
Wohl wahr. Nur - sein Problem ist genau der einizige vernünftige Anwendungsfall für goto.
Nicht umsonst haben viele Sprachen break&continue mit Parametern, so dass folgendes ginge:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| for i:= 0 to 100 do begin for j:= 0 to 100 do begin if foo then break(2); end; end; |
Ist aber in Delphi nicht, und damit bleibt lediglich Flag-gewurschtel übrig. Macht den Code nicht gerade lesbarer...
HenryHux - Sa 02.10.10 19:10
Ehrlich gesagt ist durch Goto mein Code nicht unübersichtilcher geworden.
Normalerweise hatte ich auf die nächste Prozedur gezeigt mit zb read3.click.
Jetzt steht da halt goto r3.
Für mich eingeltich besser so und schneller vor allem.
jaenicke - Sa 02.10.10 19:13
Martok hat folgendes geschrieben : |
Nicht umsonst haben viele Sprachen break&continue mit Parametern, so dass folgendes ginge: |
Lesbarer ist es IMHO aber trotzdem nicht. :nixweiss:
Man selbst durchschaut es, aber wenn man sich sowas dann später anschaut, versteht man es auch kaum auf Anhieb und jemand anderes...
Wenn man sprechende Bezeichner wählt, sind Flags durchaus sinnvoll für diesen Zweck. Dann kann man direkt lesen wann die Schleife abgebrochen wird und muss eben nicht in der Prozedur suchen wo da rausgesprungen wird.
Klar, bei Mini-Prozeduren wo es z.B. eine einzelne Suchschleife ist oder so, da überblickt jeder auch ein break auf Anhieb, aber bei verschachtelten längeren Schleifen nicht.
HenryHux hat folgendes geschrieben : |
Jetzt steht da halt goto r3. |
r3 wirst du aber später auch nicht mehr verstehen. ;-)
Sprechende Bezeichner wären schon sinnvoller.
Hidden - Sa 02.10.10 20:31
Moin!
Statt goto kann man meißtens auch den Code in Prozeduren ausgliedern. Das hilft der Übersicht auf jeden Fall, selbst wenn goto ihr nicht schaden würde.
Die Ablehnung von gotos ist verbreitet, und wir wollen unseren Code ja alle noch mal anderen zeigen. Also lassen wir das Ganze^^
lg,
PS: Wenn du den Code veröffentlichen kannst, optimieren wir da gerne nochmal für dich rum(auf Länge/Stil/Benennung/Kommentare/Formatierung/was du willst) :)
HenryHux - Sa 02.10.10 22:12
Danke für das Angebot!
Aber leider kann ich den Code noch nicht veröffentlichen. Top secret :D
Ich werde bestimmt noch ein paar Monate dadran sitzen, aber ich werde es wahrscheinlich OpenSource veröffentlichen.
Bis dahin werde ich garantiert noch einige Schwierigkeiten und Fragen damit haben :D
Und immoment bin ich ja nicht der einzige Coder dran, sind ja noch ein paar andere aus dem Forum, die tatkräftig helfen.
Aber wie gesagt, ich werde es dann iwann online stellen, so wie es immoment aussieht als Freeware.
Und btw, ich finde man brauch sich nicht für ein paar gotos schämen :P
alzaimar - So 03.10.10 17:30
Ist doch ganz einfach:
Aus
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| For i:=1 to 100 do For j:=1 to 100 do for k:=1 to 100 do .... if Bedingung then break; ... |
macht man über refactoring eine eigene Prozedur:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| Procedure KomplexeSchleifen; ... begin For i:=1 to 100 do For j:=1 to 100 do for k:=1 to 100 do .... if Bedingung then exit; end; ... ... begin KomplexeSchleifen(); |
Also auslagern. ist sowieso lesbarer.
jaenicke hat folgendes geschrieben : |
Wenn man sprechende Bezeichner wählt, sind Flags durchaus sinnvoll für diesen Zweck. Dann kann man direkt lesen wann die Schleife abgebrochen wird und muss eben nicht in der Prozedur suchen wo da rausgesprungen wird. |
Bringt nur etwas, wenn das Abbruchkriterium hinterher verwendet wird. Ansonsten ist das Flag eine überflüssige und unverständlicherweise verwendete Variable.
jaenicke hat folgendes geschrieben : |
Klar, bei Mini-Prozeduren wo es z.B. eine einzelne Suchschleife ist oder so, da überblickt jeder auch ein break auf Anhieb, aber bei verschachtelten längeren Schleifen nicht.
|
Das Grundproblem ist doch, das man so viele verschachtelte Schleifen hat. Ich kenne eigentlich keinen Algorithmus, der mehr als drei verschachtelte Schleifen benötigt. Alles andere lässt sich durch Refactoring vereinfachen oder basiert auf einer falschen Datenstruktur.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!