![]() |
for-Schleife gegen über while-Schleife
Unser Lehrer hat heute gemeint, dass man es möglichst vermeiden sollte aus Schleifen zu springen. Und wenn es nötig sein sollte, sollte man es im Schleifenkopf machen, also eine while-Schleife nehmen.
Ich habe das jetzt mal ausprobiert:
Delphi-Quellcode:
function IsPLZInArray(SonnenStundenListe: TSonnenStunden; PLZ: Integer): Boolean;
var i: Integer; CurrentPLZ: Integer; begin i := 0; Result := False; while (i < length(SonnenStundenListe) - 1) and (not Result) do begin CurrentPLZ := SonnenStundenListe[i, 0]; Result := CurrentPLZ = PLZ; Inc(i); end; end;
Delphi-Quellcode:
Also ich finde die for-Schleife leichter verständlich. Bei der while-Schleife habe ich fast einen Knoten ins Hirn bekommen, wegen der Abbruchbedingung. Das gilt für mich zumindest wenn es mehr als eine Abbruchbedingung gibt.
function IsPLZInArray(SonnenStundenListe: TSonnenStunden; PLZ: Integer): Boolean;
var i: Integer; CurrentPLZ: Integer; begin i := 0; Result := False; for I := 0 to length(SonnenStundenListe) - 1 do begin CurrentPLZ := SonnenStundenListe[i, 0]; if CurrentPLZ = PLZ then begin Result := True; Break; end; end; end; Was meint ihr? |
Re: for-Schleife gegen über while-Schleife
Das wesentliche Problem ist nur die Lesbarkeit. In deinem Beispiel müsste man zunächst den Kopf der zuletzt angeworfenen Schleife suchen um zu wissen was nun wo fortgesetzt wird. Da das Break in einem if-Block, also eine Ebene weiter eingerückt steht, kann man nicht einfach den Block als verlassenes Segment verstehen, sondern man muss u.U. eine ganze Serie von Block-Hierachien beachten.
Bei der while-Version steht gleich am Anfang "Pass mal auf, wenn das und jenes in der Schleife passiert, gehts einfach am Ende genau dieses Blocks weiter. Das war dann aber auch schon alles, und ich erwische mich selbst auch oft genug dabei Break oder Continue zu verwenden, weil es im Schreibfluss finde ich leichter zu erdenken ist. In manchen Fällen gehe ich dann aber doch nachher noch mal an die Schleifen und baue sie um - einfach nur weil's schöner zu lesen ist, und unsere Programme teilweise auch noch nach 2-3 Jahren mit Änderungswünschen belegt werden, wo es dann doch ganz gut ist schnell zu verstehen was man sich damals so alles dabei gedacht hat =) |
Re: for-Schleife gegen über while-Schleife
Meine ganz klare Meinung: Die for-Variante ist in diesem Fall besser, weil vorher bekannt ist bis wo die Schleife maximal läuft.
Von der Lesbarkeit oder dem Schreiben des Codes ist das für mich persönlich kein großer Unterschied, sofern der Code ordentlich formatiert ist. Mehrfache Abbruchbedingungen benutze ich oft und die zu entwerfen ist auch kein Problem. Der Grund ist ein anderer. Die for-Schleife kann vom Compiler sehr gut optimiert werden. Und der Zielwert ist bekannt und muss nicht jedesmal überprüft werden. Der Compiler kann ein passendes Register auswählen und die Zählweise optimieren. Das geht bei der While-Schleife nicht. Es muss jedesmal die vorgegebene Bedingung überprüft werden. Ergebnis mit while:
Delphi-Quellcode:
Ergebnis mit for:
Unit162.pas.36: i := 0;
004532C9 33F6 xor esi,esi Unit162.pas.37: Result := False; 004532CB 33DB xor ebx,ebx 004532CD EB0D jmp $004532dc Unit162.pas.41: CurrentPLZ := SonnenStundenListe[i]; 004532CF 8B45FC mov eax,[ebp-$04] 004532D2 8B3CB0 mov edi,[eax+esi*4] Unit162.pas.42: Result := CurrentPLZ = PLZ; 004532D5 3B7DF8 cmp edi,[ebp-$08] 004532D8 0F94C3 setz bl Unit162.pas.43: Inc(i); 004532DB 46 inc esi Unit162.pas.39: while (i < length(SonnenStundenListe) - 1) and (not Result) do 004532DC 8B45FC mov eax,[ebp-$04] 004532DF E84022FBFF call @DynArrayLength // !!! Wird jedesmal aufgerufen 004532E4 48 dec eax 004532E5 3BF0 cmp esi,eax 004532E7 7D04 jnl $004532ed 004532E9 84DB test bl,bl 004532EB 74E2 jz $004532cf Unit162.pas.45: end; 004532ED 33C0 xor eax,eax
Delphi-Quellcode:
Aus dem Grund ist die for-Schleife deutlich besser geeignet unter der Voraussetzung, dass man wie in diesem Fall vorher weiß wie lange die Schleife laufen soll (von der Abbruchbedingung abgesehen). In der Schleife muss dann hier in diesem Fall nur noch eine selbst geschriebene Bedingung ausgeführt werden.
Unit162.pas.53: Result := False;
0045333E 33DB xor ebx,ebx Unit162.pas.55: for I := 0 to length(SonnenStundenListe) - 1 do 00453340 8B45FC mov eax,[ebp-$04] 00453343 E8DC21FBFF call @DynArrayLength // !!! Wird nur einmal aufgerufen 00453348 48 dec eax // überflüssig, siehe unten folgender Quelltext 00453349 85C0 test eax,eax 0045334B 7C15 jl $00453362 0045334D 40 inc eax 0045334E 33D2 xor edx,edx Unit162.pas.57: CurrentPLZ := SonnenStundenListe[i]; 00453350 8B4DFC mov ecx,[ebp-$04] 00453353 8B3C91 mov edi,[ecx+edx*4] Unit162.pas.58: if CurrentPLZ = PLZ then 00453356 3BF7 cmp esi,edi 00453358 7504 jnz $0045335e Unit162.pas.60: Result := True; 0045335A B301 mov bl,$01 Unit162.pas.61: Break; 0045335C EB04 jmp $00453362 Unit162.pas.63: end; 0045335E 42 inc edx Unit162.pas.55: for I := 0 to length(SonnenStundenListe) - 1 do 0045335F 48 dec eax 00453360 75EE jnz $00453350 Unit162.pas.64: end; 00453362 33C0 xor eax,eax Am optimalsten sieht das Ergebnis aus, wenn man direkt High benutzt, dann spart man auch noch das -1:
Delphi-Quellcode:
function IsPLZInArrayFor2(SonnenStundenListe: TSonnenStunden; PLZ: Integer): Boolean;
var i: Integer; CurrentPLZ: Integer; begin Result := False; for I := 0 to High(SonnenStundenListe) do begin CurrentPLZ := SonnenStundenListe[i]; if CurrentPLZ = PLZ then begin Result := True; Break; end; end; end;
Delphi-Quellcode:
Unit162.pas.70: Result := False;
0045333E 33DB xor ebx,ebx Unit162.pas.72: for I := 0 to High(SonnenStundenListe) do 00453340 8B45FC mov eax,[ebp-$04] 00453343 E8E421FBFF call @DynArrayHigh 00453348 85C0 test eax,eax 0045334A 7C15 jl $00453361 0045334C 40 inc eax 0045334D 33D2 xor edx,edx Unit162.pas.74: CurrentPLZ := SonnenStundenListe[i]; 0045334F 8B4DFC mov ecx,[ebp-$04] 00453352 8B3C91 mov edi,[ecx+edx*4] Unit162.pas.75: if CurrentPLZ = PLZ then 00453355 3BF7 cmp esi,edi 00453357 7504 jnz $0045335d Unit162.pas.77: Result := True; 00453359 B301 mov bl,$01 Unit162.pas.78: Break; 0045335B EB04 jmp $00453361 Unit162.pas.80: end; 0045335D 42 inc edx Unit162.pas.72: for I := 0 to High(SonnenStundenListe) do 0045335E 48 dec eax 0045335F 75EE jnz $0045334f Unit162.pas.81: end; 00453361 33C0 xor eax,eax |
Re: for-Schleife gegen über while-Schleife
Aus irgendeinem verstaubten Lehrbuch sitzt bei mir auch noch im Kopf: Kein Goto, Kein Break, Kein Exit
Also auch quasi die Aussage, wenn eine For-Schleife ein Break braucht, verwende While. Ich halte mich schon lange nicht mehr daran, weil es richtig verwendet, nicht die Lesbarkeit zerstört. Man soll ja sowieso Schleifeninhalte möglichst klein halten, dann ist der Zusammenhang nicht weit auseinander gerissen. Mit etwas Erfahrung ist es selbst mir bei beiden Versionen leicht gefallen, den Sinn der Schleifen zu verstehen. Auch Exit am Funktions-Anfang, um sich eine weitere If-Ebene zu sparen, halten ich persönlich für Ok. Die Beispiele von jaenicke fand ich jetzt auch sehr lehr- und aufschlussreich. Denn ohne mir Gedanken zu machen, hatte ich noch immer die Meinung, das While der For-Schleife in Sachen Geschwindigkeit überlegen ist. Das das falsch ist, sieht man so sehr schön. Auch der Vorteil von High() hätte ich nicht erwartet. Btw: Macht die vorherige Zuweisung auf die lokale Variable CurrentPLZ überhaupt Sinn? PS: Goto gehört natürlich trotzdem in kein Pascal-Code, war auch bei mir noch nie nötig. |
Re: for-Schleife gegen über while-Schleife
Unser Informatik-Professor rät auch ganz klar zur For-Schleife, wenn diese verwendet werden kann. Als Vorteile nannte er hauptsächlich, dass keine Endlosschleifen entstehen können und die Lesbarkeit oft einfacher ist. Trotz break oder continue.
Grüße, Matze PS: Ein sehr kurzer Beitrag, aber man sieht mal die gegensätzlichen Meinungen von Dozenten. PPS: Sebastians Beitrag ist wirklich interessant. Dass "High" (minimal) schneller ist als die "-1"-Variante, wusste ich nicht. |
Re: for-Schleife gegen über while-Schleife
Zitat:
So, erst hatte ich meine for-Schleifen zu while-Schleifen gemacht und nach jaenickes Beitrag wieder zurück gebaut, weil er mich doch bestätigt hat. Da wären zum einem die technischen Gründe und dann noch die gefühlten Gründe, wie sie Satty67 erwähnt hat. Aber einen hätte ich noch:
Delphi-Quellcode:
Mit
function GetNextLowerPLZFromList(var PLZ: Integer; PLZSunHours: TPLZSunHours): Integer;
begin // Raises EPLZNotInList exception if no lower PLZ can be found Result := PLZ; while (not IsPLZInList(PLZ, PLZSunHours)) and (PLZ <> -1) do begin Dec(PLZ); Result := PLZ; end; if PLZ = -1 then Raise EPLZNotInList.Create(rsENotInList); end;
Delphi-Quellcode:
Ich denke, hier muss man eine while-Schleife nehmen. Aber die Abbruchbedingungen verursachen mir noch Kopfschmerzen. Das sieht irgendwie komisch bzw. umständlich aus.
Type
TPLZSunHours = array[0..4, 0..1] of Integer; const PLZSunHours: TPLZSunHours = ((34126, 1200), (34127, 2100), (54123, 1000), (80459, 5210), (90785, 1500)); Zur Aufgabe: Gegeben ist eine zwei dimensionale Liste mit PLZ Zahlen und Sonnenstunden pro Jahr. Nach Eingabe der Postleitzahl sollen die zugehörigen Sonnenstunden ausgegeben werden. Ist die PLZ nicht in der Liste, soll die nächst niedrigere genommen werden. Wird keine PLZ gefunden soll -1 ausgegeben werden. Aus der -1 habe ich eine Exception gemacht, aber das ist nebensächlich. (Das ist ein Aufgabe aus der Abschlussprüfung für Fachinformatiker. Dort sollte das allerdings nur in Pseudocode gelöst werden.) Beim Schreiben des Codes habe ich mich versucht an die Regeln für sauberen Code zu halten, wie es in Clean Code dargestellt wird. |
Re: for-Schleife gegen über while-Schleife
Zitat:
Code:
Ohne die Zuweisung kann der Wert bei ecx+edx*4 auch direkt mit dem Wert im esi Register verglichen werden. Da aber meistens mehr mit der Variablen gemacht wird als ein simpler Vergleich, habe ich es so gelassen.
mov edi,[ecx+edx*4]
cmp esi,edi Denn meistens wird mehrfach auf den Eintrag im Array zugegriffen in der Schleife, was dann viel mehr Aufwand ist. Deshalb ist es meistens mit Zwischenvariable schon besser, also wollte ich das nicht ausgerechnet ansprechen, auch wenn es hier einen Assemblerbefehl mehr kostet. Zitat:
Delphi-Quellcode:
Sinnvoller wäre aber ja wohl eine Betrachtung des nächstkleineren Arrayeintrags statt alle Postleitzahlen durchzuprobieren.
function GetNextLowerPLZFromList(var PLZ: Integer; PLZSunHours: TPLZSunHours): Integer;
begin // Raises EPLZNotInList exception if no lower PLZ can be found Result := PLZ; while (not IsPLZInList(Result, PLZSunHours)) and (Result <> -1) do Dec(Result); if Result = -1 then Raise EPLZNotInList.Create(rsENotInList); end; Dafür müsstest du nur einmal dein Array durchgehen und alle vorhandenen Postleitzahlen betrachten. Das geht selbst bei einem unsortierten Array vermutlich schneller. Schließlich werden in deinem Array ja nicht so viele Postleitzahlen sein. |
Re: for-Schleife gegen über while-Schleife
Zitat:
darf ich fragen, was du machst? Machst du eine Fortbildung oder sowas in der Art? Bei deinem Wissen, Deine Tuts und deine Non-VCL-Programme fällt mir es schwer zu glauben, dass du hier a) von einem Lehrer im klassischen Sinne redest und b) Dich ernsthaft mit den "Grundlagen" von Pascal beschäftigst. Versteh mich nicht falsch, wenn dich deine Frage - zurecht - interessiert ist das ok. Mir ging es nur darum, dass du sagtest, dass dein Lehrer das meinte. Wenn du magst, dann erzähl mir (oder uns) doch, was du momentan machst, und wofür du einen Lehrer hast, der dir Delphi erklärt ;) Freundliche Grüße |
Re: for-Schleife gegen über while-Schleife
Schau
![]() Zitat:
Dann wird es aktuell vermutlich das 3. und letzte Lehrjahr sein. ;) |
Re: for-Schleife gegen über while-Schleife
Luckie, erlich gesagt habe ich keine Ahnung, was du für ein Problem mit derartigen Abbruchbedingungen in while-Schleifen hast ;)
Das ist ein absolutes Standardkonstrukt... |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:20 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz