![]() |
Über den Umgang mit Boolean
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo!
Nun möchte ich mich auch mal an einem kurzen Tutorial versuchen Über den Umgang mit Boolean Ein Tutorial von Dietmar Brüggendiek Die Beispiele wurden mit Delphi 5 Standard geschrieben. Wenn ich mich in den Delphi-Foren so umschaue, finde ich immer wieder Sources, die - gelinde gesagt - nicht so ganz richtig sind. Man findet häufig Folgendes:
Delphi-Quellcode:
:wiejetzt: Was soll das denn werden, wenn es fertig ist? Die Syntax ist da doch ganz eindeutig:
if BoolVar=true then
IrgendWas;
Delphi-Quellcode:
Ein logischer Ausdruck kann dabei ein Vergleich sein, z.B.
IF <logischer Ausdruck> THEN
anweisung [ELSE anweisung];
Delphi-Quellcode:
Es kann aber auch ein logischer Wert (Variablentyp Boolean) direkt da stehen:
if IntVar=5 then
IrgendWas;
Delphi-Quellcode:
oder auch eine Verknüpfung von logischen Ausdrücken.
if BoolVar then
IrgendWas; Der Vergleich "BoolVar=True" kann sogar kräftig in die Hose gehen! Bei einem normalen Boolean ist es so, daß "True" den Ordinalwert "1" hat. Bei ByteBool, WordBool und LongBool hat "True" normalerweise den Ordinalwert "-1". Einige Windows-API-Aufrufe liefern ein Ergebnis vom Typ BOOL. Dabei gilt für den einen Fall "Ergebnis=0" und im anderen Fall "Ergebnis<>0". Ein Wert ungleich 0 muß aber nicht unbedingt 1 oder -1 sein! Das Ergebnis: die Variable ist bei <>0 möglicherweise weder true noch false! Ein Vergleich mit "false" ist nur überflüssig, führt aber nicht zu fehlerhaftem Programmverhalten. In einem IF-Statement wird jedoch jeder Wert <> 0 als True betrachtet. Mal ein Beispiel: Schreiben wir eine Funktion, die uns einen Integer aus einem String liest und eine Fehler-Variable setzt, wenn der String keine gültige Zahl enthält. Dazu verwenden wir das gute alte "Val" - da brauchen wir uns nicht um Exceptions zu kümmern und sparen Aufwand und Rechenzeit.
Delphi-Quellcode:
Die Zuweisung an aFehler ist zwar nicht empfohlen, aber sie funktioniert! Code enthält 0, wenn die Umwandlung möglich war oder die Position des Fehlers im String. Dadurch ist der Ordinalwert der Boolean-Variablen entweder =0 (false) oder <>0 (gilt als true). Nutzen wir diese Funktion jetzt mal auf die falsche Weise:
function GetInt(const aSatz:string; var aFehler:Boolean):Integer;
var Code:Integer; begin Val(aSatz, Result, Code); aFehler:=Boolean(Code); end;
Delphi-Quellcode:
Wenn wir das Ganze nun so aufrufen:
procedure Test(const aSatz:string);
var Wert:Integer; Fehler:Boolean; begin wert:=GetInt(aSatz, Fehler); if Fehler=true then ShowMessage(aSatz + ': Eingabefehler!') else if Fehler=false then ShowMessage(aSatz + ': Wert: '+IntToStr(Wert)) else ShowMessage(aSatz + ': Hupps!'); end;
Delphi-Quellcode:
bekommen wir nacheinander die Meldungen:
Test('123');
Test('a234'); Test('1asfg');
Code:
Klar, im 3. Fall ist der Fehler in Position 2 und 2 ist nicht gleich Ord(True) !
123: Wert: 123
a234: Eingabefehler! 1asfg: Hupps! Kaum macht man's richtig, funktioniert es auch schon! Wir ersetzen die Prozedur "test" durch diese: Edit: Uuups - das war doch der falsche Code - hier muß ja auch der 3. Fall hin!
Delphi-Quellcode:
und erhalten das richtige Ergebnis:
procedure Test(const aSatz:string);
var Wert:Integer; Fehler:Boolean; begin wert:=GetInt(aSatz, Fehler); if Fehler then ShowMessage(aSatz + ': Eingabefehler!') else if not Fehler then ShowMessage(aSatz + ': Wert: '+IntToStr(Wert)) else ShowMessage(aSatz + ': Hupps!'); end;
Code:
Wie oben schon erwähnt, gibt es diese Rückgabewerte bei Windows-API-Aufrufen. Das etwas haarige Beispiel ist also gar nicht so weit hergeholt.
123: Wert: 123
a234: Eingabefehler! 1asfg: Eingabefehler! Was soll nun das schon wieder werden, wenn's fertig ist?
Delphi-Quellcode:
Bei solchem Gestümpere rollen sich mir die Fußnägel auf! Warum denn der leere Block? Achja, es soll was getan werden, wenn die Variable nicht true ist. Aber vielleicht sollte man sich ja mal mit den Boolean-Operatoren beschäftigen. Da könnte man herausfinden, daß es NOT gibt!
if BoolVar then
begin end else Irgendwas;
Delphi-Quellcode:
bewirkt dasselbe, ist kürzer und für andere Programmierer leichter zu lesen.
if not BoolVar then
IrgendWas; (Anmerkung: sollte das Programm noch nicht fertig sein und der Block später noch gefüllt werden, habe ich nichts gesagt. Aber dann sollte man doch einen Kommentar setzen, daß hier noch was fehlt!) Weiter geht es in unserer beliebten Reihe: "Warum Einfach wenn's auch kompliziert geht?" mit Folgendem:
Delphi-Quellcode:
Der Ausdruck "IntVar=5" ist doch schon ein Boolean und kann der BoolVar direkt zugewiesen werden! Wir schreiben also
if IntVar=5 then
BoolVar:=true else BoolVar:=false;
Delphi-Quellcode:
und gut ist!
BoolVar:=IntVar=5;
Im umgehehrten Fall müssen wir auch nicht mit
Delphi-Quellcode:
arbeiten. Da hilft
if IntVar=5 then
BoolVar:=false else BoolVar:=true;
Delphi-Quellcode:
Allerdings: Die Klammern darf man hier nicht vergessen!
BoolVar:=not (IntVar=5);
Auswertung boolscher Ausdrücke Bei Boolean-Ausdrücken kann es natürlich vorkommen, daß das Ergebnis vorzeitig feststeht:
Delphi-Quellcode:
Wenn die 1. Variable 3, die 2. 9 und die 3. 567 ist, braucht der letzte Vergleich nicht mehr ausgeführt werden, denn es ist ja schon ein Ausdruck wahr.
if (IntVar1=5) or (IntVar2=9) or (IntVar3=567) then
IrgendWas; Standardmäßig führt Delphi hier eine Optimierung durch, indem die Auswertung dann abgebrochen wird, wenn das Ergebnis bereits feststeht. Das kann man aber abschalten. Dazu gibt es den Compilerschalter $B bzw. $BOOLVAR. Die verkürzte Auswertung von Boolean-Ausdrücken führt aber zu einer weiteren bösen Falle:
Delphi-Quellcode:
ist eine ganz gefährliche Sache. Bei optimiertem Boolean wird der 2. Teil nicht mehr ausgewertet; ohne Optimierung jedoch ergibt sich bei AnsiString die Situation, daß der Index außerhalb des Wertebereichs liegen kann.
if (Length(StringVar)>=5) and (StringVar[5]='x') then
IrgendWas; Solange man in seiner Unit/Komponente den B-Compilerschalter richtig einstellt, sollte beim Austausch von Quellcode nichts passieren. Wenn man sich jedoch auf die Einstellung des Compilers verläßt, ist man verlassen und erhält dann vom Anwender Rückmeldungen, daß das Ganze "manchmal" fehlerhaft läuft. SIMULA67 kennt bedingte Ausdrücke. Damit könnte man das Problem sinngemäß so lösen:
Code:
Das Beispiel ist syntaktisch nicht ganz richtig, aber in SIMULA habe ich schon lange nicht meht programmiert. Außerdem ist dieser Sprachmix für Delphi-Programierer leichter zu verstehen.
IF (IF Length(string_var) >= 5 THEN False ELSE string_var[5] = 'x') THEN
irgend_was; In Delphi sollte man hier nicht sparen:
Delphi-Quellcode:
und man ist für alle Fälle gerüstet.
if Length(StringVar)>=5 then
if StringVar[5]='x' then IrgendWas; Man kann die Auswertung ja in einer Funktion kapseln. Sinn und Unsinn der verkürzten Auswertung Im obigen Beispiel erscheint die verkürzte Auswertung sinnvoll. Was ist aber, wenn mehrere Funktionen aufgerufen werden, die jeweils noch etwas Weitergehendes erledigen? Ein Beispiel: Berechnen des Rauminhaltes eines Quaders. Wir lesen Länge, Breite und Höhe als Integer-Werte aus 3 TEdit. Wenn ein Feld kein Integer enthält, soll das zum TEdit gehörende TLabel rot eingefärbt werden, um den Fehler anzuzeigen. Werden alle Werte korrekt eingegeben, wird ein Ergebnisfeld auf den Rauminhalt gesetzt.
Delphi-Quellcode:
Haben wir das Kurzschlußverfahren eingeschaltet (B- oder BOOLVAR OFF), wird nur der erste Fehler gemeldet. Bei vollständiger Auswertung (B+ oder BOOLVAR ON) werden alle Fehler gemeldet.
procedure TForm1.bRechnenClick(Sender: TObject);
var Laenge, Breite, Hoehe: Integer; begin if LeseEingabe(lLaenge, Laenge) or LeseEingabe(lBreite, Breite) or LeseEingabe(lHoehe, Hoehe) then lErgebnis.Caption:='Fehler!' else lErgebnis.Caption:=IntToStr(Laenge*Breite*Hoehe); { Das erste fehlerhafte Edit oder das erste Edit aktivieren } if lLaenge.Color<>Color then TEdit(lLaenge.FocusControl).Setfocus else if lBreite.Color<>Color then TEdit(lBreite.FocusControl).Setfocus else if lHoehe.Color<>Color then TEdit(lHoehe.FocusControl).Setfocus else eLaenge.Setfocus; end; function TForm1.LeseEingabe(const aLabel: TLabel; var aWert: Integer): Boolean; var Code: Integer; begin Val(TEdit(aLabel.FocusControl).Text, aWert, Code); Result:=Code<>0; if Result then aLabel.Color:=clRed else aLabel.Color:=Color; end; Im Anhang ist das vollständige Projekt mit gleichzeitiger Auswertung in beiden Varianten. Glücklicherweise ist der B-Schalter lokal, kann also in einer Datei beliebig oft gesetzt werden. Die linke Spalte wird mit B+, die rechte mit B- ausgewertet. Man sieht bei Eingabefehlern in mehreren Feldern schön den Unterschied. Von der Ergonomie her sollte natürlich das Verhalten der linken Spalte benutzt werden. Für Benutzer älterer Compiler habe ich das Formular im älteren Verfahren (also nicht als Text) gespeichert. Wer nicht compilieren will, kann sich auch die Echse laden. Das Ganze ist so einfach wie möglich gehalten - es soll ja nur das Prinzip gezeigt werden. Anmerkung: Bitte nicht über die fehlende Typprüfung beim Cast nach TEdit meckern! Das Ganze wird zur Designzeit festgelegt, so daß Fehler wie fehlendes FocusControl oder ein falsches Feld im FocusControl bereits beim ersten Test auffallen werden. Wer natürlich nach dem Motto vorgeht: "Es compiliert, wir können es ausliefern", hat Pech gehabt :mrgreen: Gruß Dietmar Brüggendiek |
Re: Über den Umgang mit Boolean
Da ist noch eine Falle.
Delphi-Quellcode:
Hier verlaesst man sich darauf das Compilermagic es schon richten wird.
aFehler:=Boolean(Code);
Besser ist
Delphi-Quellcode:
Das ist auch deutlicher in seiner Aussage.
aFehler := Code <> 0;
Zusatzlich moechte ich noch die wirklich schlimme Praxis des leeren else erwaehnen. Da gebe ich mal kein Beispiel, damit es keiner nachmacht. |
Re: Über den Umgang mit Boolean
Zitat:
Was bringt denn bitte ein leeres else? //Edit: Hab' nachgedacht: Das waren die Leute, die not nich kennen, oder? :stupid: Schwachsinn... |
Re: Über den Umgang mit Boolean
Es erlaubt in einer if-else-Verschachtelung einen leeren begin-end-Block wegzulassen.
|
Re: Über den Umgang mit Boolean
Zitat:
Der Cast auf Boolean soll doch ein Cast sein, der die Daten nicht verändert, sondern nur den Typ. Das ist doch gerade der Kernpunkt des ersten Teil des Tutorials! Würde man deine Konstruktion verwenden, bekäme man in diesem Fall keine Probleme mit einem expliziten Vergleich auf true oder false. |
Re: Über den Umgang mit Boolean
Irgendwas muss der Compiler beim Cast auf Boolean machen.
Ein Boolean enthaelt nur True oder False mit Ord(True) = 1 und Ord(False) = 0. Die Konsequenz ist das entweder fuer den Cast oder Ord Code generiert werden muss. |
Re: Über den Umgang mit Boolean
Ich finde das Tutorial gut geschrieben, mich regt dieses "= true" auch immer auf :mrgreen: . Den leeren Block vor else habe ich aber (zum Glück) noch nie gesehen, das ist ja wirklich grausig :stupid: .
Delphi-Quellcode:
Hier finde ich die Einrückung auf den ersten Blick etwas verwirrend, besser wäre IMHO:
begin
wert:=GetInt(aSatz, Fehler); if fehler=true then ShowMessage(aSatz + ': Eingabefehler!') else if Fehler=false then ShowMessage(aSatz + ': Wert: '+IntToStr(Wert)) else ShowMessage(aSatz + ': Hupps!'); end;
Delphi-Quellcode:
begin
wert:=GetInt(aSatz, Fehler); if fehler=true then ShowMessage(aSatz + ': Eingabefehler!') else if Fehler=false then ShowMessage(aSatz + ': Wert: '+IntToStr(Wert)) else ShowMessage(aSatz + ': Hupps!'); end; Zitat:
Code:
Und als Ergänzung: boolsche Kurzausdrücke in C#:
List<string> StringList = null;
if (StringList == null ? false : StringList.Count > 0) Console.WriteLine(StringList[0]);
Code:
List<string> StringList = null;
// Löst eine System.NullReferenceException aus, da "&" einfach nur ein logischer Operator ist if (StringList != null & StringList.Count > 0) Console.WriteLine(StringList[0]); // Löst keine Exception aus, "&&" ist ein "logischer Bedingungsoperator" oder ""abkürzender" Operator" // Entspricht dem Code von oben mit dem "?:" Operator if (StringList != null && StringList.Count > 0) Console.WriteLine(StringList[0]); |
Re: Über den Umgang mit Boolean
Zitat:
Delphi-Quellcode:
ShowMessage(InttoStr(Ord(Boolean(2))));
|
Re: Über den Umgang mit Boolean
Zitat:
|
Re: Über den Umgang mit Boolean
Also, ich muss ganz ehrlich sagen, dass ich gelegentlich eine Abfrage mit "= false" mache. Und zwar immer dann, wenn ich eine mehrmals verschachtelte Abfrage mach, am besten noch mit (veraschachtelten) Funktionsaufrufen. Bevor ich mich dann inmitten der Klammern und der Operatorenrangfolge verirre, oder auch nur zuviel Zeit verschwende, mich noch einen Moment damit auseinanderzusetzen, schreib ich einfach ein "= false" dahin. Das finde ich dann einfach leichter, und ich sehe auch keine besonderen Nachteile darin.
Ich persönlich finde jedenfalls, dass es übertrieben ist, sich darüber "aufzuregen", dass manche "if irgendwas = true" schreiben. Das Ganze hat auch etwas mit Programmierstil zu tun. Und wer es so übersichtlicher findet, soll es halt übersichtlicher finden. Auf die 6 Zeichen die dabei zusätzlich getippt werden (meinetwegen bei 100 maligem Aufruf 600 Zeichen) kommt es auch nicht mehr drauf an. Bei dem Rest geb ich dir jedoch Recht. Alles in allem ist das jedoch ein nettes Tutorial :thumb: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:31 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 by Thomas Breitkreuz