MH> Und da man goto nicht braucht, ...
MH> Im Prinzip reicht while als Schleifenkonstrukt.
Ich habe das auch schon versucht. Aber ich komme nicht
ganz klar damit. Wenn ich nun 2 verschachtelte Schleifen
habe und mit der inneren beide abbrechen moechte. Wie bitte
muss ich das erstellen ohne goto zu verwenden? (Ich bin leider
Anfaenger und habe das Problem - fuer mich - noch nicht
loesen koennen).
Mit freundlichen Gruessen
Robert
--- Maximus 3.01
* Origin: Online: 22:00 - 08:00 ++43-1-523 52 85 (V.34/V.FC) (2:240/4150.3)
ES>>> Es ist inzwischen mathematisch nachweisbar, dass _jede_
ES>>> Aufgabenstellung, die ueberhaupt loesbar ist, auch ohne GOTO loesbar
ES>>> ist.
MR>> Ja. Klar. Es ist auch nachweisbar, das _jede_ Aufgabenstellung, die
MR>> ueberhaupt loesbar ist, auch ohne Pascal loesbar ist. =8-)=)
> Das geht aber am Problem vorbei.
auch wenn ich es ungern tue, hier muß ich Michael recht geben.
ES>>> GOTO ist ein
ES>>> Befehl, der in keinem, aber absolut keinem Programm irgendeine
ES>>> Berechtigung haette.
MR>> ... und damit geht die Diskussion in die theologische Ebene ueber. Ich
MR>> sehe diese Sache viel relaxter: Wenn Du goto brauchst, nimmst Du goto.
> Du brauchst es eben nicht. Wenn du ein goto (scheinbar) brauchst, ist das
> schon ein Beweis fuer ein mangelhaftes Design, wenn du das Goto nicht durch
> eine saubere if- oder while-Konstruktion ersetzen kannst.
Wozu sich diese erzieherischen Pascal-Thesen aufladen?
Die Anweisungen break, continue und return (inmitten der Funktion) sind
eigentlich nichts anderes, als versteckte goto's, da sie den Block auf
"unnatürlichem" Wege verlassen. In C++ heißen die neuen goto's
"Exceptions". ;)
MR>> PS: Die Formulierung ist zwar nicht von mir, aber man koennte "for,
MR>> while, do-while, continue, break und derlei Dinge mehr als goto's im
MR>> Abendkleid betrachten".
So ist es.
> Koennte man schon. Aber diese goto's kannst du ja direkt ersetzen. Und
> "andere" goto's sind eben kapitale Strukturfehler im Design. Und da ist das
> Problem. Das Design muss zu 250% gut sein, sonst hast du nachher immense
> Schwierigkeiten mit Wartung und Pflege. Das faengt unter Umstaenden schon
> beim Debuggen an!
Willst Du ein Programm fertigstellen, oder willst Du für deine Sourcen
Schönheitspreise gewinnen?
In vielen Fällen ist der Aufwand einfach nicht gerechtfertigt, ein goto
durch einen anderen Konstrukt zu ersetzen.
Ciao
Sönke
PS: goto habe ich selbst erst 2x benutzt.
"My homepage is my castle"
am 20.07.96 10:46 erzähltest Du Michael
RHH> Ich habe das auch schon versucht. Aber ich komme nicht ganz klar
RHH> damit. Wenn ich nun 2 verschachtelte Schleifen habe und mit der
RHH> inneren beide abbrechen moechte. Wie bitte muss ich das erstellen ohne
RHH> goto zu verwenden? (Ich bin leider Anfaenger und habe das Problem -
RHH> fuer mich - noch nicht loesen koennen).
int fertig = FALSE;
for (/*init*/; !fertig && (/*abbruchbedingung*/); /*Abschluß*/) {
/* igendwas */
for (init; /*abbruchbedingung*/; /*Abschluß*/) {
/* irgendwas */
if (fehler) { fertig = TRUE; break;
/* noch was */
}
/* noch was, das IMMER gemacht werden soll */
/* kann auch fehlen */
if (fertig) continue; /* break; geht auch */
/* noch mehr code */
}
if (!fertig) {
/* die Schleifen wurden BEIDE bis zum Ende bearbeitet */
} else {
/* die innere Schleife wurde vorzeitig abgebrochen */
}
Tschau/Bye
Herbert email: h...@softpro.de
Member #53 of Team/OS2 Germany
--- Sqed/32 1.12/r00196
* Origin: Schont die Umwelt: Vermeidet DOSen (2:2476/493)
MR> Ausserdem zeigt es sehr schoen, dass man Dinge, die "theoretisch" nicht
MR> gebraucht werden, in der Praxis recht nuetzlich sein koennen. Wie goto.
MR> Oder Pascal =8-)=)
Das goto brauchst du aber eben auch in der Praxis nicht.
MR> Beispiel: Aus irgendwelchen Gruenden hast Du 'ne ganze Reihe von
MR> Funktionen, die im Fehlerfall immer die Befehlssequenz bla-laber-blubb
MR> abarbeiten muessen. Solche Fehler koennen an mehreren Stellen innerhalb
MR> einer Funktion auftreten, so dass jedesmal bla-laber-blubb faellig wird.
MR> Nun koennte die Sache mit folgender Struktur uebersichtlicher gestalten:
MR> DieseFunktion ( ... )
MR> {
MR> if(ParameterUngueltig)
MR> goto DieseFunktion_ErrOut;
MR> ...
MR> if(BaggerseeZuKalt)
MR> goto DieseFunktion_ErrOut;
MR> ...
MR> return AllesEasyEy;
MR> DieseFunktion_ErrOut:
MR> bla;
MR> laber;
MR> blubb;
MR> return Fehlercode;
MR> }
MR> Auch im Fehlerfall hat diese Funktion dann formal nur einen Ausgang (altes
MR> Design-Prinzip einmal anders... thihi...)
Das macht man erstens mal viel eleganter ueber ein paar Praeprozessor-Makros.
Die Konstruktion, die du anwendest, ist zweitens formal nichts anderes als eine
break-Konstruktion. Und drittens: Wenn du von vorneherein mit ner richtigen
Analysemethode arbeitest, ist diese Konstellation gar nicht moeglich. Es geht
gar nicht, beispielsweise mit SA so eine Konstruktion zu erzeugen, weil du
bereits in der Analysephase auf einen ganz anderen Weg kommen wirst.
MR> Noe. Eher fuer ein falsch definiertes "richtiges Design".
MR> "Zu enge Kopplung" der Begriffe "goto" und "richtiges Design". (ooops -
MR> schon wieder ein geklautes Prinzip)
Eben nicht. Wenn du strukturierte Designmethoden verwendest (Beispiel: SADT
oder im Datenbankbereich die Methode von Scheer) kannst du ueberhaupt nicht auf
ein goto kommen. Ein goto laesst sich mit diesen Methoden naemlich nicht
darstellen. Da du z.B. bei SA am Schluss in der Mini-Spez ueblicherweise mit
Nassi-Shneidermann-Diagrammen arbeitest, fliegen solche Dinge bereits in der
Phase raus. Wenn du dann trotzdem ein goto einsetzt, hast du was anderes
programmiert als du designt hast - auch wieder ein strukturfehler. Selbst das
beliebte "break" kann in einem solchen Fall gar nicht entstehen, weil es vom
Design schlicht nicht vorgesehen (und auch nicht gebraucht!) wird.
MR> Na ja... wer zum Beispiel die obige Funktionsstruktur nicht versteht, hat
MR> imho ganz andere Probleme...
Wenn die Funktion in ein groesseres Projekt eingebettet ist, ist das recht
schnell sehr unverstaendlich. Du musst mal ueber den Tellerrand von
30-Zeilen-Programmen raussehen.
MR> Noe. Du machst keine Fallunterscheidung. Das ist das Problem. Der
MR> Zeitaufwand lohnt sich nicht, wenn's um Quickies geht.
Quickies haben in professionellen Entwicklungen nichts zu suchen. Eben
aufgrund der Lebensdauer der Software. Ich erleb das grad selber im Geschaeft.
Man macht ne Q&D-Loesung fuer irgend ein Problem, dann wird da jahrelang dran
angestueckelt und am Schluss hast du einen riesen Oschi von Muell-Code, der
noch nicht mal sauber dokumentiert ist. Aber mit Software Engineering hat das
dann nichts mehr zu tun. Und da entstehen dann die Probleme. Anstatt dass man
sich gleich am Anfang mal hinsetzt und eine ordentliche Analyse und ein
ordentliches Design macht, was effektiv auf die Dauer betrachtet um mindestens
den Faktor 10 billiger kommt.
MR> Manche Quickies wie z.B. Unix sind immer noch in Gebrauch. Stimmt.
Eben.
MR> Noe. Mit goto rettet man sich normalerweise aus einer verzwickten Lage
MR> heraus, die anderweitig nur durch komplexe Umstellungen im Code zu
MR> bewaeltigen waere (Zeit, Kosten, fehleranfaellig).
So eine Lage entsteht bei einem ordentlichen Design eben gar nicht erst, das
ist ja gerade der Gag. Wenn du da hinkommst, hast du bereits einen Fehler
bewiesen. Und den nur zu umschiffen ist nicht nur unsauber, sondern auch
gefaehrlich, denn du hast dann auch keine Garantie mehr dafuer, dass dein
Design ueberhaupt funktioniert.
MR> Wenn im Design schon ersichtlich ist, ob irgendwo gotos auftauchen oder
MR> nicht, ist es nicht abstrakt genug.
In einem guten Design nach einer strukturierten Methode (wenns nicht grad
Jackson oder HIPO ist) kann kein goto auftauchen. Dazu ist das Design da. Was
du meinst, ist die Analysephase, die tatsaechlich sehr abstrakt ist. Aber
selbst da steht (zumindest z.B. bei SA) am Schluss bereits ne
Mini-Spezifikation, und im Design werden bei einigen Methoden sogar
Hardware-Anforderungen oder aehnliches festgehalten. Das Design soll nicht
abstrakt sein, sondern die konkrete Problemloesung lediglich unabhaengig vom
verwendeten Werkzeug (Sprache, Rechnerumgebung etc.) darstellen. Und Da
erkennst du sehr wohl, ob du ein goto brauchst oder nicht. Ein vollendetes
Design waere z.B. eine Reihe von Struktogrammen, die die einzelnen Funktionen
des Programms beschreiben, in Verbindung mit einem Data Dictionary, das die
Datenbestaende und -Verbindungen bzw. Beziehungen aufzeigt (z.B. in Form eines
ER-Modells). Da kannst du jede Zeile Code direkt draus ablesen. Und die
Implementierung beschraenkt sich dann absolut auf das Umsetzen des Designs in
eine konkrete Umgebung. Das ist bei einem guten Design normalerweise eine
schlichte Uebersetzungsarbeit.
ES>> Das faengt unter
ES>> Umstaenden schon beim Debuggen an!
MR> Ich habe in meiner Schublade ein nettes kleines Prograemmelchen, das
MR> wuerde ohne goto noch nicht mal den Weg bis in den Debugger schaffen.
MR> (Genauer gesagt, die goto's sind so abgedreht, dass ich sie nur in
MR> Assembler schreiben konnte - es trifft's also eigentlich nicht genau. Aber
MR> Du kannst Dich drauf verlassen, dass diese Spruenge aus der Sicht der
MR> Struktur-Paepste genauso "igittebah" sind wie goto :)
MR> Hm - beim Greppen stiess ich gerade auf folgende Zeile:
MR> goto ICantBelieveIUsedAGoToStatement;
MR> :),
MR> Michael
MR> -!- CrossPoint v3.02 R
MR> ! Origin: Du kannst ueberholen... die Wolken brechen auf...
(2:2453/30.77)
MfG, Erhard!
--- FleetStreet 1.12.1 NR
* Origin: Gib GATES keine Chance =)B==o) (2:2487/3001.65)
ES>> Du brauchst es eben nicht. Wenn du ein goto (scheinbar)
ES>> brauchst, ist das schon ein Beweis fuer ein mangelhaftes
ES>> Design,
RH> Nope. Ich benutze GOTO zu genau einem Zweck: im Fehlerfall Sprung zum
RH> Funktionsende. Das ist IMHO lesbarer als zig if/case Konstrukte, die
RH> dann alle direkt vor dem return terminieren.
So, und wenn du dann an einer bestimmten Stelle zwischen deinem goto und dem
Funktionsende noch was einfuegen musst, was auch im Fehlerfall zur Ausfuehrung
kommen soll, dann kannst du deine ganze Implementierung ueber den Haufen
schmeissen. Uebersichtlich wird das ganze uebrigens einfach dadurch, dass man
die Laenge der einzelnen Funktionen auf 30-50 LOC beschraenkt (ausgenommen
Tabellenfunktionen, die nur aus einem einzigen langen Case-Konstrukt bestehen
oder andere derartige "Fleiss"-Funktionen, die strukturell nur einen Block
haben). Die if/case-Konstrukte sehen wirklich auf den ersten Blick umstaendlich
aus, aber wenn du nachher weitere Unterscheidungen einbauen musst, hast du es
mit denen viel leichter, als wenn du mit goto's kaempfen musst, die du nach x
Jahren sehr wahrscheinlich gar nicht mehr alle findest.
Am 20.07.96 schrieb Robert H. Hanko:
RHH> Wenn ich nun 2 verschachtelte Schleifen
RHH> habe und mit der inneren beide abbrechen moechte. Wie bitte
RHH> muss ich das erstellen ohne goto zu verwenden?
int ende1=0;
while (!ende1 && weitere_bedingungen)
{
...
while (bedingungen)
{
...
if (ende_bedingung)
{
ende1=1;
break;
}
...
}
}
Servus, Michael.
--- FleetStreet 1.16.1+
* Origin: FleetStreet - where I want to go today (2:2490/2520.17)
Nur dass die Struktur eines Programmes kein Selbstzweck ist.
Genaugenommen ist es total schnurz, ob Dein Programm
strukturiert ist - das Designziel, das erreicht werden soll ist
nicht "Struktur des Quelltextes", sondern "Leichte Wartbarkeit"
und "Uebersichtlichkeit". Die Struktur ist dabei nur ein Mittel
zur Erreichung dieses Zweckes, aber keine hl. Lehre. Das
verlieren die Verfechter von reinem ISO-Pascal manchmal aus den
Augen.
Und unter diesem Kritierium schneidet Pascal dann gelegentlich
nicht besonders gut ab. Wie Kernighan demonstriert, fuehrt die
Armut an solchen verkuerzenden Kontrollstrukturelementen wie
"break" dazu, dass Programmierer aus Bequemlichkeit auf die
korrekte, langatmige Formulierung des Codes verzichten.
Stattdessen schreiben sie die verkuerzte, aber leider unter der
Auswertungsreihenfolge von ISO-Pascal *falsche* Form hin.
Das ist ja dann auch genau der Grund, warum man in Turbo-Pascal
Dinge wie short circuit evaluation, Schleifenabbrueche und so
weiter als Sprachelemente eingefuehrt hat: Damit aus einer
Wandtafelsprache eines Algorithmenprofessors ein Werkzeug wird,
das im taeglichen Gebrauch ueberleben kann.
>Die einzige Stelle, wo in meinen Augen ein break in C-Code was zu suchen hat,
>ist deshalb in der switch()-Konstruktion.
Konstrukte wie
funktion()
{
error_t error;
result_t result;
if (!waechter) {
error = GRUNDa;
return error;
}
if (!waechter2)
error = GRUNDb;
return error;
}
while(arbeitsbedingung)
{
if (fehler)
{
error = GRUND1;
goto errorhandler;
}
if (anderer fehler)
{
error = GRUND2;
goto errorhandler;
}
arbeitsanweisungen;
}
return result;
errorhandler:
handle(&error);
return error;
}
sind gaengige Redewendungen in C und das seit Jahrzehnten.
Programmcode dieser Form zieht sich zum Beispiel durch von den
Quellen fuer den UNIX V7 Kernel und die dazugehoerenden
Utilities bis hin zu aktuellen Versionen von 4.4BSD und Linux.
Und das, obwohl sich der Programmierstil ansonsten ziemlich
radikal gewandelt hat (das ist eine andere, nicht minder
interessante Geschichte).
Solche Redewendungen sind ausserdem auch sehr uebersichtlich,
weil sie naemlich die Fehler- und Abbruchbedingungen, auf die
getestet wird, in einer leicht zu lesenden und auch raeumlich
zusammengehoerenden Form aufschreiben.
Wenn das nicht in die Ideologie der sogenannten strukturierten
Programmierung passt, dann ist das ein Grund, ueber die
Leistungsfaehigkeit dieser Ideologie nachzudenken. Auch das ist
getan worden. Das Resultat ist zum Beispiel das formalisierte
Exception-Handling in Objective-C, C++, Java oder den
Microsoft-C-Erweiterungen (siehe Jeffrey Richter, "Advanced
Windows", Microsoft Press, Kapitel 14, "Structured Exception
Handling").
In allen diesen Sprachen werden sehr aehnliche Konstrukte zur
Handhabung dieses extrem haeufigen Spezialfalls vorgestellt,
die ueber das o.a. Beispiel noch hinausgehen und die nach den
Prinzipien der "strukturierten Programmierung" boese sind.
Das dem so ist, ist nicht verwunderlich. "Strukturierte
Programmierung" ist ein Konzept aus den Zeiten von BASIC, COBOL
und FORTRAN (man sehe sich an, von welchem Datum "GOTO's
considered harmful" ist). Damals war das Hauptproblem, Code so
ans Rennen zu bekommen, dass er ueberhaupt Leistung brachte.
Dieses Problem wurde von den Informatik-Schlipstraegern damals
als "die Softwarekrise" bezeichnet.
Heute hat man mit der Erzeugung von laufendem Code und dem
Verstaendnis von grundlegenden Kontroll- und Datenstrukturen
keine Probleme mehr. Die heutige "Softwarekrise" (ja, dieses
Modewort hat sich mehr als zwei Jahrzehnte gehalten!) besteht
darin, Code zu erzeugen, der auch unter fehlerhaften
Eingabeparametern weiterlaeuft und diesen Code dann in
moeglichst vielen anderen Projekten ohne grossen Aufwand
weiterverwenden zu koennen. Dieses Problem wird von der
strukturierten Programmierung ueberhaupt nicht angesprochen -
es war damals (ausserhalb gewisser akademischer Zirkel) auch
gar nicht existent.
Und so kommt es, dass die Lehre der strukturierten
Programmierung zwar sicherlich sehr nuetzlich ist, aber bei
weitem nicht vollstaendig und in einigen Punkten fuer den
praktichen Einsatz einfach unsinnig.
>Versuch mal in einem Programm mit mehreren break's in einer
>Schleife, das ein fremder geschrieben hat, nach 10 Jahren noch ein Statement
>unmittelbar vor dem Schleifenabbruch einzufuegen (z.B. das Ruecksetzen eines
>devices oder sowas).
Wenn Dir das hilft, dann will ich das gerne tun. Der folgende
Code ist mehr als 20 Jahre alt und stammt nicht von mir,
sondern von AT&T. Er ist Bestandteil von UNIX V7, das auf einer
PDP-11 laeuft. Ich habe ihn aus Gruenden der Uebersichtlichkeit
und aus Gruenden der Urheberrechts ein wenig verkuerzt.
Die Funktion exece() ist eine zentrale Funktion von UNIX. Sie
ist der einzige Weg, mit dem man neuen Code in das Image eines
Prozesses laden kann, d.h. sie tut in etwa das, was CHAIN in BASIC
macht: Sie laedt ein Programm nach und startet dieses.
exece() ist keine kurze Funktion: Sie ist knapp 130 Zeilen lang. Die
Kommentare sind von mir:
exece()
{
register nc;
register char *cp;
register struct buf *bp;
register struct execa *uap;
int na, ne, ns, bno, ucp, ap, c;
struct inode *ip;
/* Wenn der Name des auszufuehrenden Programms nicht
in eine Datei uebersetzbar ist: weg!
*/
if ((ip = namei(uchar, 0)) == NULL)
return;
/* Wenn auf die auszufuehrende Datei keine Zugriffsrechte
bestehen: weg!
*/
if(access(ip,IEXEC)
|| ((ip->i_mode&IFMT) != IFREG)
|| ((ip->i_mode&(IEXEC|(IEXEC>>3)|(IEXEC>>6))) == 0)) {
u.u_error = EACCES;
iput(ip);
return;
}
[ 8 Zeilen Initialisierung mit linearem Kontrollfluss geloescht ]
/* Wenn kein Speicher zum Start des Programmes zu bekommen
ist: System anhalten (das Aequivalent zu exit() im
Kernel).
*/
if ((bno = malloc(swapmap,(NCARGS+BSIZE-1)/BSIZE)) == 0)
panic("Out of swap");
[ ueber 100 Zeilen weiteren Code mit aehnlicher
Struktur geloescht ]
Das ist genau die Form Code, die ich weiter oben skizziert
habe. Sie ist gaengig, leicht zu lesen und leicht zu warten -
selbst nach mehr als 20 Jahren noch.
An anderen Stellen in V7 UNIX findest Du massenhaft das
goto-Konstrukt, dass ich oben angedeutet habe und das
schliesslich in den Nachfolgesprachen von C zu einem richtigen
Exception-Handler mutiert ist.
Genau solche Strukturen sind in ISO-PASCAL (im Gegensatz zu
Turbo-Pascal) aber kaum zu konstruieren. Zum einen musst Du
if's unnoetig verschachteln, weil keine short circuit
evaluation existiert:
if ( (zeiger) && (zeiger->komponente)) ...
ist kein gueltiges Pascal. Zum anderen ist das Konzept "ein
entry, ein exit" bei richtigen Funktionen einfach bloedsinnig.
Du glaubst doch nicht im ernst, dass der Code, den ich da oben
gezeigt habe, uebersichtlicher wird, wenn man statt
if (waechter)
{
something;
return errorcode;
}
if (another waechter)
{
something else;
return errorcode;
}
nutritional value;
return errorcode;
gezwungen wird
if (waechter) {
something;
} else {
if (another waechter) {
something else;
} else {
nutritional value;
}
}
return errorcode;
zu schreiben, nur um den single entry, single exit-Konzept
genuege zu tun? Und das nicht bei dem o.a. Spielzeug-Code,
sonern bei Produktions-Sourcen mit etwa einem halben bis einem
ganzen Dutzend solcher Waechter?
Kristian
--
Kristian Koehntopp, Wassilystrasse 30, 24113 Kiel, +49 431 688897
"Und das T ist Markenzeichen der Telekom... (klar, man erkennt es ja
sofort an tief heruntergezogenen Mundwinkeln des Querstrichs...)"
-- Rainer Zocholl in tp.forum
--- FIDOGATE 3.9.3
* Origin: Fido-Gateway @ white.schulung.netuse.de (2:240/2123.59)
*** Am Montag 22. Juli 1996 um 00:27 schrieb Herbert Rosenau an Robert H.
Hanko:
HR> int fertig = FALSE;
HR> for (/*init*/; !fertig && (/*abbruchbedingung*/); /*Abschluß*/) {
Das bringt mich auf eine allgemeine Bemerkung:
es ist ziemlich unelegant, in einer Schleifenbedingung permanent boole'sche
Operatoren zu verwenden. Sie arbeiten zwar fix (wenig Takte), aber man sich das
leicht ersparen, sogar ohne Verlust an Lesbarkeit, wenn man in etwa:
int nichtfertig=TRUE; /* invertierter Startinhalt zu vorher */
for (init; nichtfertig && abbruch; inkrement) {
if (irgendwas) nichtfertig=FALSE;
}
Das erspart in diesem simplen Beispiel pro Schleifendurchlauf ein "not".
Tschau...Thomas
--- E3-32/1.11-32/2.50+
* Origin: Die TeX-Box +49-6034-930021 V.34 -930022 ISDN 24h (2:244/1130.42)
RH>> Nope. Ich benutze GOTO zu genau einem Zweck: im Fehlerfall
RH>> Sprung zum Funktionsende. Das ist IMHO lesbarer als zig
RH>> if/case Konstrukte, die dann alle direkt vor dem return
RH>> terminieren.
ES> So, und wenn du dann an einer bestimmten Stelle zwischen
ES> deinem goto und dem Funktionsende noch was einfuegen musst,
ES> was auch im Fehlerfall zur Ausfuehrung kommen soll,
Da kann nichts zur Fehlerausfuehrung kommen. Ich meine harte
Fehler, mit denen die Funktion nichts mehr anfangen kann. zB
Database nicht vorhanden oder nicht zugaenglich. Was soll dann
eine Funktion noch tun, die dazu da ist, einen/mehere Selects
abzusetzen ? Es gibt in diesem Falle nicht anderes, als einen
Statuscode zu setzen und die Funktion zu verlassen. Was in einem
solchen Fall dann weiter zu tun ist, muss der Aufrufer
entscheiden, schliesslich hat er die Funktion "Daten lesen"
aufgerufen und muss nun sehen, wie damit fertig wird, dass er dir
Daten nicht bekommt. "Daten lesen" kann seine Aufgabe schlicht
und ergreifend nicht erfuellen, hat aber keine Entscheidung
darueber zu treffen, wies nun wieter geht oder ob etwas anderes
zu tun ist.
ES> kannst du deine ganze Implementierung ueber den Haufen
ES> schmeissen.
Nein.
ES> denen viel leichter, als wenn du mit goto's kaempfen musst,
ES> die du nach x Jahren sehr wahrscheinlich gar nicht mehr alle
ES> findest.
Ich habe es mit GOTO's wesentlich leichter, die nur zu einem
einzigen Zweck da sind: Naemlich tief verschachtelte IF/Case
Konstrukte zu vermeiden. Wenn es keinen Sinn mehr macht, in
dieser Funktion zu verbleiben, dann verlasse ich sie. Und zwar
ueber _einen_ Punkt und nicht ueber n Returns (wie vielleicht
jetzt der eine oder andere einwenden moechte :-) ). Mehrere
Returns in einer Funktion sind naemlich allerschlechtester
Programmierstil. Ich erreiche mit meinem Goto naemlich genau den
_einen_ Ausgang, den eine Funktion haben sollte. Und ist noch
Aufraeumarbeit zu leisten, so steht das zwischen goto und return.
Und wird damit immer aufgerufen. Fuer mich fuehrt das zwanghafte
vermeiden von gotos zu unleserlichen Programmen.
au revoir
Rainer
--- CrossPoint v3.11 R
* Origin: Noctambule lunatique qui clignote dans le soir (2:2435/610.21)
SM> Wozu sich diese erzieherischen Pascal-Thesen aufladen?
SM> Die Anweisungen break, continue und return (inmitten der Funktion) sind
SM> eigentlich nichts anderes, als versteckte goto's, da sie den Block auf
SM> "unnatuerlichem" Wege verlassen. In C++ heissen die neuen goto's
SM> "Exceptions". ;)
Also, erstens mal sind das keine Pascal-Themen, sondern Programmierthemen, die
von der Sprache recht unabhaengig sind. Die Tatsache, dass bestimmte Dinge in
einer Sprache (C ist hier ein typisches Beispiel) zulaessig sind, heisst noch
lange nicht, dass sie auch sinnvoll oder sauber sind. Exceptions kannst du auch
nur bedingt mit break- oder goto-Konstruktionen vergleichen, da ihnen ein
voellig anderes Konzept zugrunde liegt. Ausserdem ist das Arbeiten mit
Exceptions ebenso wie das Arbeiten mit goto tatsaechlich sehr gefaehrlich, wenn
man nicht gaanz vorsichtig programmiert.
SM> Willst Du ein Programm fertigstellen, oder willst Du fuer deine Sourcen
SM> Schoenheitspreise gewinnen?
Ich will Sourcen erstellen, die man auch in 10 Jahren noch warten kann, ohne
dabei gefahr zu laufen, durch ein uebersehenes goto vor den eingefuegten
Anweisungen tagelang zu debuggen.
SM> In vielen Faellen ist der Aufwand einfach nicht gerechtfertigt, ein goto
SM> durch einen anderen Konstrukt zu ersetzen.
Es ist laengst nachgewiesen, dass der Aufwand nur ein scheinbarer ist. Und der
Aufwand, die goto's beim Warten des Programms nachher zu suchen und evtl. dann
die ganze "goto-Struktur" wieder umschmeissen zu muessen bzw. der Aufwand, der
durch mit diesen Konstrukten provozierte Wartungsfehler entsteht, ist um ein
vielfaches hoeher. Ich kann dir gerne mal so eine Source mit massenhaft breaks
zur Verfuegung stellen. Wenn so ein goto von ner mehrfach Verschachtelten
Schleife ans Funktionsende springt (und das an mehreren Stellen), und du musst
evtl. 3 Verschachtelungsebenen weiter aussen noch ne Anweisung einbauen, dann
hast du damit ein riesen Problem. Und wie gesagt: Bei einem sauber erstellten
Design sind solche Konstruktionen schon Konstruktionsbedingt ausgeschlossen,
d.H. du kannst gar nicht auf die Idee kommen, sie zu verwenden.
*Elegant* macht man das mit einer Spracherweiterung: Structured
Exception Handling (SEH). So zu finden in C++, Objective-C,
Java und einigen Microsoft-Erweiterungen von C.
RH> Da kann nichts zur Fehlerausfuehrung kommen. Ich meine harte
RH> Fehler, mit denen die Funktion nichts mehr anfangen kann. zB
RH> Database nicht vorhanden oder nicht zugaenglich. Was soll dann
RH> eine Funktion noch tun, die dazu da ist, einen/mehere Selects
RH> abzusetzen ?
Zum Beispiel die zwei anderen Datenbanken, die sie zuvor geoeffnet hat, wieder
sauber schliessen (aber nur, wenn sie geoeffnet wurden!) oder das Filesystem
flushen.
RH> Es gibt in diesem Falle nicht anderes, als einen
RH> Statuscode zu setzen und die Funktion zu verlassen. Was in einem solchen
RH> Fall dann weiter zu tun ist, muss der Aufrufer entscheiden, schliesslich
RH> hat er die Funktion "Daten lesen" aufgerufen und muss nun sehen, wie
RH> damit fertig wird, dass er dir Daten nicht bekommt. "Daten lesen" kann
RH> seine Aufgabe schlicht und ergreifend nicht erfuellen, hat aber keine
RH> Entscheidung darueber zu treffen, wies nun wieter geht oder ob etwas
RH> anderes zu tun ist.
RH> Fuer mich fuehrt das zwanghafte vermeiden von gotos zu
RH> unleserlichen Programmen.
Es ist eben _nicht_ zwanghaft. Obiges Beispiel:
Nach 10 Jahren soll das Prg dahingehend geaendert werden, dass deine Funktion
diese Daten aus zwei DB's zusammensuchen muss. Wenn die neue DB vor der bereits
implementierten geoeffnet werden soll, muss sie im Falle eines Fehlers beim
Oeffnen der alten wieder sauber zugemacht werden. Bereits da kommt deine
Konstruktion ins Schwimmen. UNd sowas kann nun wirklich Aufwand erzeugen. Und
wie bereits erlaeutert: Mit ner ordentlichen Design-Methode kannst du gar nicht
auf goto's kommen. Und ich will doch wohl hoffen, dass du deine Programme auf
Analyse und Design aufbaust und dieses Design dann auch implementierst, sonst
hat die ganze Diskussion natuerlich keinen Sinn, denn dann produzierst du eh
potentiellen Spaghetti-Code. Das selbe Ergebnis gibts z.B. auch, wenn mehrere
Speicherbloecke alloziert werden und einer davon nicht mehr zu kriegen ist oder
bei anderen Betriebsmitteln. Vielleicht zur Verdeutlichung ein Code-Beispiel:
Deine Variante:
while (Bedingung1)
{
int xyz;
...
if (xyz == 3) DBOeffnen(ZusatzDB);
while (Bedingung2)
{
switch (gewaehlte_datenbank)
{
case KUNDEN:
...
if (OeffneKunDB == FEHLER) goto fehler;
break;
case LIEFERANTEN:
if (OeffneLieDB == FEHLER) goto fehler;
break;
case ARTIKEL:
if (OeffneArtDB == FEHLER) goto fehler;
break;
}
}
}
...
fehler:
return FEHLER;
So, nun folgendes Wartungsproblem:
-im Fehlerfall muss die ZusatzDB geschlossen werden, falls sie vorher
geoeffnet wurde, und zwar in der aeusseren Schleife
(wo sie auch geoeffnet wurde)
Natuerlich gibts dafuer ne Masse Loesungen, aber die strukturell sauberste ist
eben die Entfernung der goto's. Diese Form von Goto's war vielleicht zu
COBOL-Zeiten noch wichtig, aber in C oder Pascal sollte man sowas wirklich
tunlichst unterlassen. Steht uebrigens auch in vielen mir bekannten
Code-Richtlinien, die nicht alle ausschliesslich von der Uni stammen, sondern
auch teilweise aus Industriebetrieben. Und es hat sich auch bewaehrt.
*** Am Mittwoch 24. Juli 1996 um 00:00 schrieb Rainer Huebenthal an Erhard
Schwenk:
RH>>> Nope. Ich benutze GOTO zu genau einem Zweck: im Fehlerfall
RH>>> Sprung zum Funktionsende. Das ist IMHO lesbarer als zig
RH>>> if/case Konstrukte, die dann alle direkt vor dem return
RH>>> terminieren.
Dijkstra und Wirth haben mittlerweile auch festgestellt, dass GOTO
in bestimmten Situationen brauchbar ist.
Der Artikel von Dijkstra ("Goto considered harmful") besteht im
wesentlichen darin, dass der Sprung mitten _in_ einen Block
verdammt wird, also etwa
i=175;
goto hallo;
for (i=32; i<127; ++i) {
hallo:
printf("%c ",i);
>Addresse: Rainer_H...@p21.f610.n2435.z2.fido.de
Sagt mal, spinn ich, oder lesen wir hier jetzt Fidomails über ein Internetgate?
cu
Torsten
SM>> Wozu sich diese erzieherischen Pascal-Thesen aufladen?
SM>> Die Anweisungen break, continue und return (inmitten der Funktion) sind
SM>> eigentlich nichts anderes, als versteckte goto's, da sie den Block auf
SM>> "unnatuerlichem" Wege verlassen. In C++ heissen die neuen goto's
SM>> "Exceptions". ;)
> Also, erstens mal sind das keine Pascal-Themen,
ich schrieb "Thesen"!
SM>> Willst Du ein Programm fertigstellen, oder willst Du fuer deine Sourcen
SM>> Schoenheitspreise gewinnen?
> Ich will Sourcen erstellen, die man auch in 10 Jahren noch warten kann, ohne
> dabei gefahr zu laufen, durch ein uebersehenes goto vor den eingefuegten
> Anweisungen tagelang zu debuggen.
Wie ich bereits erwähnte (hast Du aber nicht mitgequotet), habe ich selbst
"goto" höchstens zweimal eingesetzt. Mir ging es eher um die Verwendung
von "break" und "continue".
SM>> In vielen Faellen ist der Aufwand einfach nicht gerechtfertigt, ein goto
SM>> durch einen anderen Konstrukt zu ersetzen.
> Es ist laengst nachgewiesen, dass der Aufwand nur ein scheinbarer ist.
Sorry, diesen "Nachweis" stelle ich hiermit in Frage.
> Und der Aufwand, die goto's beim Warten des Programms nachher zu suchen
> und evtl. dann die ganze "goto-Struktur" wieder umschmeissen zu muessen
> bzw. der Aufwand, ...
Halt mal an!
Niemand von den "goto-Befürwortern" hier im Netz hat vor, sämtliche
Kontrollstrukturen durch goto's zu ersetzen. Es geht hier nur um den
Einsatz einer einzigen wohlplazierten "goto"-Anweisung, um z.B. eine
tiefverschachtelte Schleife zu verlassen.
Ich programmiere nicht erst seit gestern, aber ich weiß nicht, wie
> der durch mit diesen Konstrukten provozierte Wartungsfehler
> entsteht, ist um ein vielfaches hoeher.
beim Einsatz _eines_ goto's Wartungsfehler entstehen können.
> Ich kann dir gerne mal so eine Source mit massenhaft breaks zur
> Verfuegung stellen.
Spar dir das bitte. Niemand will auch "massenhaft" break's einsetzen.
> Und wie gesagt: Bei einem sauber erstellten Design sind solche
Konstruktionen schon
> Konstruktionsbedingt ausgeschlossen, d.H. du kannst gar nicht auf die
> Idee kommen, sie zu verwenden.
Meinetwegen.
Ciao
Sönke
Ich frage mich allerdings, ob ein guter Compiler dieses NOT nicht gleich in die
Abbruchbedingung integriert. Schließlich haben die meisten Prozessoren doch
passende Jxx-Befehle. Und den Compare-Befehl kann man ohnehin nicht einsparen.
Siegmund
TS> int nichtfertig=TRUE; /* invertierter Startinhalt zu vorher */
TS> for (init; nichtfertig && abbruch; inkrement) {
TS> if (irgendwas) nichtfertig=FALSE;
TS> }
TS> Das erspart in diesem simplen Beispiel pro Schleifendurchlauf ein "not".
Ist einerseits richtig, andererseits entspricht die umgekehrte Variante aber
mehr der menschlichen Denkweise und erscheint zumindest mir dadurch
verstaendlicher. Und das "not" ist in den meisten Programmen eigentlich
sch...egal, denn Rechenzeit-kritisch ist auf den heutigen Rechnern eh fast
nichts mehr.
TS> im wesentlichen darin, dass der Sprung mitten _in_ einen
TS> Block verdammt wird, also etwa
TS>
TS> i=175;
TS> goto hallo;
TS>
TS> for (i=32; i<127; ++i) {
TS> hallo:
TS> printf("%c ",i);
TS> }
TS>
Wenn mir jemand solchen Code vorlegt, war er die laengste Zeit in
meinem Projektteam. Wer sowas macht, kann evtl Tokens so
anordnen, dass der Compiler nicht mehr mosert, aber programmieren
kann man das ja nicht mehr nennen.
am 23.07.96 23:11 erzähltest Du Herbert
HR>> int fertig = FALSE;
HR>> for (/*init*/; !fertig && (/*abbruchbedingung*/); /*Abschluß*/) {
TS> Das bringt mich auf eine allgemeine Bemerkung:
TS> es ist ziemlich unelegant, in einer Schleifenbedingung permanent
TS> boole'sche Operatoren zu verwenden. Sie arbeiten zwar fix (wenig
TS> Takte), aber man sich das leicht ersparen, sogar ohne Verlust an
TS> Lesbarkeit, wenn man in etwa:
Das kommt letztenlich auf einige Faktoren an. In dem kurzen beispiel ist das
auch nur vereinfacht ausgedrückt. Erweitert man das ganze zu 'solange kein
Fehler (ferig ist 0)' und Fehlerzustände durch einen Wert != 0 kennzeichnet,
kann man gleichartige Bearbeitungen nach der Schleife sehr wohl wiederum
zusammenfassen - und wenn es nur die Ausgabe einer Fehlermeldung ist.
Man muß wissen, daß 0 == FALSE und != 0 (also alles Mögliche eben TRUE ist.
Wenn der eigentliche Abbruchgrund wiederum eine eigene Verarbeitung hat, kommt
man um den '!-Operator nicht herum, es sei denn man zieht Langschrift vor.
K> Genaugenommen ist es total schnurz, ob Dein Programm
K> strukturiert ist - das Designziel, das erreicht werden soll ist
K> nicht "Struktur des Quelltextes", sondern "Leichte Wartbarkeit"
K> und "Uebersichtlichkeit". Die Struktur ist dabei nur ein Mittel
K> zur Erreichung dieses Zweckes, aber keine hl. Lehre. Das
K> verlieren die Verfechter von reinem ISO-Pascal manchmal aus den
K> Augen.
Wenn du dich streng an "ISO-Pascal" wie du es nennst haelst, ist es durchaus
moeglich, Wartbarkeit und Struktur zu erreichen. Die Frage ist, wie du deine
Programme entwickelst. Wenn du ordentliche Analyse- und Design-Methoden (ich
weiss, das wiederholt sich) nimmst, _kannst_ du gar kein goto einbauen.
K> Und unter diesem Kritierium schneidet Pascal dann gelegentlich
K> nicht besonders gut ab. Wie Kernighan demonstriert, fuehrt die
K> Armut an solchen verkuerzenden Kontrollstrukturelementen wie
K> "break" dazu, dass Programmierer aus Bequemlichkeit auf die
K> korrekte, langatmige Formulierung des Codes verzichten.
K> Stattdessen schreiben sie die verkuerzte, aber leider unter der
K> Auswertungsreihenfolge von ISO-Pascal *falsche* Form hin.
K> Das ist ja dann auch genau der Grund, warum man in Turbo-Pascal
K> Dinge wie short circuit evaluation, Schleifenabbrueche und so
K> weiter als Sprachelemente eingefuehrt hat: Damit aus einer
K> Wandtafelsprache eines Algorithmenprofessors ein Werkzeug wird,
K> das im taeglichen Gebrauch ueberleben kann.
Diese scheinbaren Verkuerzungen erzeugen nicht nur nach meiner Erfahrung in
Wahrheit nur Verlaengerungen bei den Wartungsarbeiten. Sie wurden auch nicht
eingefuehrt, weil sie notwendig sind, sondern weil einige goto-verliebte BASIC-
und C-Programmierer nicht in der Lage waren, den Gedanken der strukturierten
Programmierung richtig zu verstehen und umzusetzen. Schleifenabbrueche sind in
groesseren Programmen ne absolute Sauerei, und wenn du jemals 200 kb Quellcode
warten musstest, der damit durchsetzt war, dann weisst du auch warum.
K> sind gaengige Redewendungen in C und das seit Jahrzehnten.
K> Programmcode dieser Form zieht sich zum Beispiel durch von den
K> Quellen fuer den UNIX V7 Kernel und die dazugehoerenden
K> Utilities bis hin zu aktuellen Versionen von 4.4BSD und Linux.
K> Und das, obwohl sich der Programmierstil ansonsten ziemlich
K> radikal gewandelt hat (das ist eine andere, nicht minder
K> interessante Geschichte).
Erstens sind diese Konstruktionen uralt, zweitens haben sie mit Struktur nichts
zu tun und drittens sind genau das die Konstruktionen, die heute eine Wartung
z.B. alter COBOL-Software nahezu unmoeglich und deshalb eine Neuentwicklung
vieler Softwarepakete noetig machen. Genau das ist auch der Grund, warum die
strukturierte Programmierung nicht zum gewuenschten Erfolg wiederverwendbaren
Codes fuehrte: weil sich zuviele Leute eben nicht an die Strukturierungen
halten koennen. Und (im Gegensatz zur Meinung vieler Pascal-Freaks) Struktur
findet zunaechst mal im Kopf des Entwicklers statt, nicht in der Sprache.
K> Solche Redewendungen sind ausserdem auch sehr uebersichtlich,
K> weil sie naemlich die Fehler- und Abbruchbedingungen, auf die
K> getestet wird, in einer leicht zu lesenden und auch raeumlich
K> zusammengehoerenden Form aufschreiben.
Sie sind strukturmaessiger Datenmuell, weil sie unerwartete
Programmverzweigungen erzeugen, die in groesseren Quelltexten nur sehr schwer
zu ueberblicken sind. Wenn du bei der Wartung so ein goto oder break oder
continue uebersiehst, rennst du dem tagelang hinterher.
K> Wenn das nicht in die Ideologie der sogenannten strukturierten
K> Programmierung passt, dann ist das ein Grund, ueber die
K> Leistungsfaehigkeit dieser Ideologie nachzudenken.
Es ist eher ein Anlass, ueber dein Verstaendnis von Struktur nachzudenken.
Strukturierte Programmierung heisst, mit moeglichst wenigen
Grundkonstruktionen, die noch dazu einfach zu verstehen sind, alle gegebenen
Probleme loesen. Das ist ein Paradigma, das sich absolut bewaehrt hat, und wenn
du wirklich ernsthaft Software entwickelst und entsprechende Methoden
verwendest, kannst du aus diesen Strukturen schon rein methodisch auch gar
nicht mehr ausbrechen. Diese Tatsachen aendern aber nichts an der
Leistungsfaehigkeit der "Ideologie" strukturierte Programmierung.
K> Auch das ist
K> getan worden. Das Resultat ist zum Beispiel das formalisierte
K> Exception-Handling in Objective-C, C++, Java oder den
K> Microsoft-C-Erweiterungen (siehe Jeffrey Richter, "Advanced
K> Windows", Microsoft Press, Kapitel 14, "Structured Exception
K> Handling").
Die OOD-maessig hoechst umstritten ist und Quellcodes bereits heute schwer
wartbar werden liess. Bis heute ist umstritten, ob Exceptions nun
strukturmaessig sinnvoll oder gefaehrlich sind. Und nach dem, was ich bisher
sah, welche Exceptions von manchen Leuten verbrochen werden, halte ich sie eher
fuer verboten.
K> Das dem so ist, ist nicht verwunderlich. "Strukturierte
K> Programmierung" ist ein Konzept aus den Zeiten von BASIC, COBOL
K> und FORTRAN (man sehe sich an, von welchem Datum "GOTO's
K> considered harmful" ist). Damals war das Hauptproblem, Code so
K> ans Rennen zu bekommen, dass er ueberhaupt Leistung brachte.
K> Dieses Problem wurde von den Informatik-Schlipstraegern damals
K> als "die Softwarekrise" bezeichnet.
Die Softwarekrise war wohl etwas mehr als ein Performance-Problem. (wenn sie
ueberhaupt existierte). Aber auch die OO stammt aus den spaeten 60er Jahren,
sie ist nur wenige Jahre aelter als die Strukturierte Programmierung. Das
Problem war auch nicht, Programme am Laufen zu halten, sondern die zunehmende
Komplexitaet der Softwarepakete in den Griff zu bekommen.
K> Heute hat man mit der Erzeugung von laufendem Code und dem
K> Verstaendnis von grundlegenden Kontroll- und Datenstrukturen
K> keine Probleme mehr. Die heutige "Softwarekrise" (ja, dieses
K> Modewort hat sich mehr als zwei Jahrzehnte gehalten!) besteht
K> darin, Code zu erzeugen, der auch unter fehlerhaften
K> Eingabeparametern weiterlaeuft und diesen Code dann in
K> moeglichst vielen anderen Projekten ohne grossen Aufwand
K> weiterverwenden zu koennen. Dieses Problem wird von der
K> strukturierten Programmierung ueberhaupt nicht angesprochen -
K> es war damals (ausserhalb gewisser akademischer Zirkel) auch
K> gar nicht existent.
Also, die wiederverwendbarkeit von Code wird in der strukturierten
Programmierung sehr wohl angesprochen, z.B. im Rahmen sauberer Designtechniken
mit Modularisierung. Und ob das Problem wirklich akademisch ist, laesst sich
daran erkennen, wieviel Zeit fuer unstrukturierte COBOL- und BASIC-Programme
auch heute noch aufgewendet werden muss und wie teuer es ist, diese Anwendungen
heute neu zu entwickeln. Wenn bei deren Codierung bereits sauber designt und
strukturiert worden waere, waere dieser Aufwand heute wesentlich kleiner. Aber
wer machte sich damals schon die Muehe, DFD's und NSD's detailliert zu
entwerfen, zumal es an entsprechenden TOols mangelte?
K> Und so kommt es, dass die Lehre der strukturierten
K> Programmierung zwar sicherlich sehr nuetzlich ist, aber bei
K> weitem nicht vollstaendig und in einigen Punkten fuer den
K> praktichen Einsatz einfach unsinnig.
Sie ist nicht nur vollstaendig, sondern auch sinnvoll. Es ist nachgewiesen,
dass jedes mit "goto" und "unstrukturierten" Mitteln loesbare Problem auch eine
strukturierte Loesung hat, darueber gibts auch ne Doktorarbeit.
K> ist kein gueltiges Pascal. Zum anderen ist das Konzept "ein
K> entry, ein exit" bei richtigen Funktionen einfach bloedsinnig.
Im Gegenteil, alles andere ist unuebersichtlich und Muell. Ich weiss nicht, wo
du programmierst, aber solcher Code wuerde bei mir in der Firma direkt in der
Tonne landen. Gottseidank.
K> zu schreiben, nur um den single entry, single exit-Konzept
K> genuege zu tun? Und das nicht bei dem o.a. Spielzeug-Code,
K> sonern bei Produktions-Sourcen mit etwa einem halben bis einem
K> ganzen Dutzend solcher Waechter?
Das ist absolut nicht komplexer, kostet kaum Rechenzeit und spart bei der
Wartung extrem Entwicklungszeit ab. Uebrigens: Auch "Produktions-Sourcen" z.B.
kaufmaennischer C/S-Datenbankanwendungen werden zumindest in meiner Firma nach
Moeglichkeit mit handlichen Funktionen strukturiert, die entweder nur eine
Strukturkomponente oder nicht mehr als etwa 50-100 Zeilen Quellcode umfassen
duerfen. Alles andere waere auch schon wieder ein Designfehler.
ps: Mit deinem Absender scheint was nicht zu stimmen.
K> *Elegant* macht man das mit einer Spracherweiterung: Structured
K> Exception Handling (SEH). So zu finden in C++, Objective-C,
K> Java und einigen Microsoft-Erweiterungen von C.
Ist aber wartungsmaessig u.U. auch ganz nett problematisch und ausserdem nicht
mehr C, sondern ne auf C aufbauende Sprache. Aber prinzipiell gilt auch hier:
Wenn du ne ordentliche Analsye und ein korrektes (und verifiziertes) Design
machst, fallen solche Konstrukte schon von Vorne herein raus, ohne dass es dir
ueberhaupt bewusst wird. Und wenn du sie nachher wieder einbaust, hast du ne
Todsuende begangen und was anderes Programmiert, als du designt hast.
MM> Vorschlag: ein break; + Setzen eines Abbruchflags in der inneren und eine
MM> Abfrage dieses Abbruchflags in der aeusseren Schleife.
MM> Oder ein goto. :-) Tu Dir da nur keinen Zwang an. ;-)
Beides mieser Stil und _verboten_ fuer einen ordentlichen Programmierer (und
bei sauberer Entwicklung mittels A- und D-Tools auch gar nicht machbar).
MM> Hi Rainer,
MM> 20 Jul 96 00:00, Rainer Huebenthal wrote to Erhard Schwenk:
MM> [...]
RH>> Nope. Ich benutze GOTO zu genau einem Zweck: im Fehlerfall Sprung zum
RH>> Funktionsende. Das ist IMHO lesbarer als zig if/case Konstrukte, die dann
RH>> alle direkt vor dem return terminieren.
MM> Auch das kann man u. U. geschickt (oder krampfhaft ;-) ) umgehen:
MM> Was man nicht alles tut, um nicht dieses verpoente "goto" zu benutzen. :-)
Was du machst, ist strukturiert betrachtet auch nicht besser. Dadurch, dass du
goto durch ein anderes Wort (hier break) ersetzt, ist der strukturfehler noch
lange nicht behoben. Der Code hat genau die gleichen Probs wie ein mit goto's
gesetzter Code. Bessere Variante siehe andere Mail von gestern. Es geht nicht
darum, das Wort "goto" zu vermeiden, sondern die Struktur "Sprung" bzw.
"Unbedingter Sprung" nicht zu verwenden. Beides ist eigentlich schon Aufgabe
der Design-Phase.
JR> Moin Erhard!
RH>>> Nope. Ich benutze GOTO zu genau einem Zweck: im Fehlerfall Sprung zum
RH>>> Funktionsende. Das ist IMHO lesbarer als zig if/case Konstrukte, die
RH>>> dann alle direkt vor dem return terminieren.
JR> ich habe dieses schema via konsequenter labelelierung in c uebernommen,
JR> wenn es wirklich nicht vernuenftig ohne gotos geht. und _das_ ist
JR> definitiv auch wartbar.
Es geht immer definitiv vernuenftig ohne gotos. Nimm ne richtige Design-Methode
(wenns nicht grad OO sein muss, z.B. SADT oder so was in der Richtung) und du
hast das Problem nicht. Und gleichzeitig hast du schon mit der Analyse und dem
Design die halbe Doku fuer deinen Code geschrieben, musst nur noch ein
Funktions- und Variablenverzeichnis anhaengen (vorausgesetzt, es existiert ein
Pflichtenheft).
ES> Zum Beispiel die zwei anderen Datenbanken, die sie zuvor
ES> geoeffnet hat, wieder sauber schliessen (aber nur, wenn sie
ES> geoeffnet wurden!) oder das Filesystem flushen.
Zwischen FuncExit (dieses Label heisst in allen meinen Funktionen
so) und dem return muss man natuerlich noch alles wieder
aufraeumen, was man so zur Initialisierung braucht. Es ist
schliesslich ziemlich uebel, am Anfang der Funktion Speicher
anzufordern, dann zu merken, dass es nicht weitergeht und
rauszuspringen, ohne diesen Speicher wieder freizugeben. Sowas
steht zB bei mir zwischen FuncExit und return.
ES> Nach 10 Jahren soll das Prg dahingehend geaendert werden,
ES> dass deine Funktion diese Daten aus zwei DB's zusammensuchen
ES> muss. Wenn die neue DB vor der bereits implementierten
ES> geoeffnet werden soll, muss sie im Falle eines Fehlers beim
ES> Oeffnen der alten wieder sauber zugemacht werden. Bereits da
ES> kommt deine Konstruktion ins Schwimmen.
nope. Eine Funktion hat nicht darueber zu entscheiden, ob die
Datenbank geoeffnet bleiben muss oder sie zu schliessen hat. Wo
bleibt deine Beschraenkung auf 50 LOC pro Funktion. Es eben eine
Funktion, die die Datenbank oeffnet, und es gibt eine(mehrere),
die sie liest, und es gibt eine, die sie schliesst. Diejenige,
welche sie liest, hat nicht darueber zu entscheiden, ob die
Datenbank geschlossen werden soll.
ES> wirklich Aufwand erzeugen. Und wie bereits erlaeutert: Mit
ES> ner ordentlichen Design-Methode kannst du gar nicht auf
ES> goto's kommen. Und ich will doch wohl hoffen, dass du deine
ES> Programme auf Analyse und Design aufbaust
Nein, ich baue meine Programme auf 3 der 4 CASE Phasen auf :
Planning, Analyse und Design. Construction lasse ich weg, da es
noch keine brauchbaren Code-generatoren gibt. Das was da
rauskommt ist Muell und muss manuell nachgebessert werden, da
kann ich es auch gleich weglassen.
ES> case ARTIKEL:
ES> if (OeffneArtDB == FEHLER) goto fehler;
ES> break;
ES> }
ES> }
ES> }
ES>
ES> ...
ES>
ES> fehler:
und hier kann man noch den Speicher freigeben (sofern er
erfolgreich alloziert wurde). Vorteil : Die exit Bedingung steht
einmal in der funktion und wird von jedem goto aus erreicht. Das
setzt natuerlich vorraus, dass die Speicherallozierung eine
strenge Bindung an die Funktion besitzt. Bei einer schwachen
Bindung gehoert das wieder in extra Funktionen.
ES> -im Fehlerfall muss die ZusatzDB geschlossen werden, falls
ES> sie vorher geoeffnet wurde, und zwar in der aeusseren
ES> Schleife (wo sie auch geoeffnet wurde)
Tritt waehrend des Lesens ein Fehler auf, so hat diese Routine
nicht darueber zu entscheiden, ob die DB geschlossenwerden muss.
Sie hat den Lesefehler "nach oben" weiterzureichen, sonst nichts.
ES> Natuerlich gibts dafuer ne Masse Loesungen, aber die
ES> strukturell sauberste ist eben die Entfernung der goto's.
Da koennen wir in 10 Jahren noch drueber streiten.
ES> tunlichst unterlassen. Steht uebrigens auch in vielen mir
ES> bekannten Code-Richtlinien,
Es steht im uebrigen auch in vielen mir bekannten Code-
Richtlinien, dass _ein_ goto an _das Ende_ der Funktion erlaubt
ist. Unter anderem in unserer Code-Richtlinie :-)
ES> von der Uni stammen, sondern auch teilweise aus
ES> Industriebetrieben. Und es hat sich auch bewaehrt.
Jou, es hat sich bewaehrt, das goto.
HR> Man muss wissen, dass 0 == FALSE und != 0 (also alles
HR> Moegliche eben TRUE ist. Wenn der eigentliche Abbruchgrund
HR> wiederum eine eigene Verarbeitung hat, kommt man um den
HR> '!-Operator nicht herum, es sei denn man zieht Langschrift
HR> vor.
Moment. Heisst das nicht so:
#define TRUE 1==1
#define FALSE !TRUE
????
Das ist dann aber ein grosser Unterschied ... :-)
Gruss Uwe
---
* Origin: Double - Box ... man goennt sich ja sonst nichts (2:246/1116)
>Deine Variante:
>
>while (Bedingung1)
> {
>[...]
> case KUNDEN:
> if (OeffneKunDB == FEHLER) goto fehler;
> break;
>[...]
> }
>
>...
>
>fehler:
> return FEHLER;
>
>
>So, nun folgendes Wartungsproblem:
Wie würdest Du das Problem denn lösen? Oder allgemein, wie beendest Du
verschachtelte Schleifen?
tschüß, Olaf
*** Am Sonntag 28. Juli 1996 um 12:54 schrieb Uwe Moosheimer an Herbert
Rosenau:
UM> Moment. Heisst das nicht so:
UM> #define TRUE 1==1
UM> #define FALSE !TRUE
UM> ????
Du solltest um Definitionen aus mehreren Token immer Klammern setzen.
#define TRUE (1==1)
#define hallo(x,y) ( (x) < (y) ? (x) : (y) )
RH> Einspruch. Sehr wohl machbar.
Dann hast du schlechte Tools. Oder du programmierst was anderes, als in deinem
Design steht.
HR>> int fertig = FALSE;
HR>> for (/*init*/; !fertig && (/*abbruchbedingung*/); /*Abschluss*/) {
TS> Das bringt mich auf eine allgemeine Bemerkung:
TS>
TS> es ist ziemlich unelegant, in einer Schleifenbedingung permanent
TS> boole'sche Operatoren zu verwenden. Sie arbeiten zwar fix (wenig Takte),
TS> aber man sich das leicht ersparen, sogar ohne Verlust an Lesbarkeit, wenn
TS> man in etwa:
Das ist ein gutes Beispiel fuer eine ueberfluessige (manuelle)
Optimierung. Sowas kann ein richtiger Compiler heutzutage selber.
Ironischerweise erzeugt BC++ 4.5 fuer die Variante while (i) sogar mehr
Code als fuer die Variante while (!i). Allerdings kommt es bei beiden zu
gleichen Ausfuehrungszeiten (wenn man die Codegroesse und auswirkungen auf
den Cache vernachlaessigt).
Fuer den Fall while (!i && j) bzw while (i && j) wird, abgesehen von ein
JE statt JNE, identischer Code erzeugt. Ich denke, dass dieses Beispiel
auf alle modernen Compiler uebertragbar ist.
Vor 15 Jahren haette das Argument vielleicht gezogen, heute wuerde ich in
diesem Fall sagen: es ist ziemlich unelegant in einer Schleifenbedingung
extra alles umzudrehen nur um den Compiler die Optimierung einer Negation
zu ersparen :-).
und tschuess ...
Matthias (Thiele Moenchengladbach)
--- CrossPoint v3.1 R
* Origin: We apologize for the inconvenience. (2:2433/445.21)
27 Jul 96 11:41, Erhard Schwenk wrote to Markus Mattes:
MM>> Vorschlag: ein break; + Setzen eines Abbruchflags in der inneren
MM>> und eine Abfrage dieses Abbruchflags in der aeusseren Schleife.
MM>> Oder ein goto. :-) Tu Dir da nur keinen Zwang an. ;-)
ES> Beides mieser Stil und _verboten_ fuer einen ordentlichen
ES> Programmierer (und bei sauberer Entwicklung mittels A- und D-Tools
ES> auch gar nicht machbar).
1.) Stil ist Geschmacksache
2.) Ein "ordentlicher Programmierer" ist undefined. (Wenn schon haettest Du
sagen muessen "... fuer einen ordentlichen Programmierer nach Erhard Schwenks
Definition..." )
3.) Weisst Du, wohin Du Dir Deine sogn. saubere Entwicklung und Deine
vielgepriesenen A- und D-Tools stecken kannst? ;-)
Wo sind bloss die ganzen Programmierer hin und wo kommen die ganzen Designer
her? Erstere bitte wiederkommen, letztere bitte in's Modefach wechseln. ;-))
PS: 3.) bitte nicht missverstehen. :-)
ByE/2,
MarKus.
--- (c) Frobozz Magic Tearline Company
* Origin: xxx (2:2454/505.69)
27 Jul 96 11:43, Erhard Schwenk wrote to Markus Mattes:
[...]
MM>> Was man nicht alles tut, um nicht dieses verpoente "goto" zu
MM>> benutzen. :-)
Nochmal mein Beispiel, damit man den Thread besser verfolgen kann:
void foo(void) {
while(1) {
if(nobrain) break;
/*...*/
if(nocat) break;
/*...*/
if(nojoke) break;
/*...*/
break;
}
}
Uebrigens besser waere:
void foo(void) {
do {
/* Wie oben */
} while(0);
}
ES> Was du machst, ist strukturiert betrachtet auch nicht besser.
Natuerlich nicht. Obiges Beispiel stammt aus einem Beispielsource eines
namhaften Soft- und Hardwareherstellers, der IMO zeigt, wie "igitt" goto fuer
manche ist, und dass durch krampfhafte Vermeidung von goto nicht unbedingt was
besseres rauskommt. Die Betonung lag auf "krampfhaft".
Das geht dann soweit, dass der Compiler des oben erwaehnten Herstellers bei
allen eingeschalteten Warungen auch sowas als Warnung ausgibt:
test.c(11:18) : warning EDC0837: goto statement encountered.
Ich konnte nicht anders als mich lachend am Boden zu waelzen. :-) Wann ist es
endlich soweit, dass Programme abbrechen mit der Meldung: "Error 0815: JMP
Instruction encountered."? ;-)
ES> Dadurch, dass du goto durch ein anderes Wort (hier break) ersetzt,
ES> ist der strukturfehler noch lange nicht behoben. Der Code hat genau
ES> die gleichen Probs wie ein mit goto's gesetzter Code.
Der Code IST der gleiche, wie der mit goto (ich habe nachgesehen). :-) break
bzw. continue SIND gotos.
ES> Bessere Variante siehe andere Mail von gestern. Es geht nicht darum,
ES> das Wort "goto" zu vermeiden, sondern die Struktur "Sprung" bzw.
ES> "Unbedingter Sprung" nicht zu verwenden.
Das ist mir alles klar, aber anscheinend vielen nicht, die goto solcherart
umgehen... wie gesagt, das Beispiel stammt nicht von mir.
Allerdings benutze auch ich goto dort, wo es (mir) passt und habe da keinerlei
Beruehrungsaengste. ;-) Evtl. liegt das daran, dass ich, bevor ich C lernte
schon Assembler programmierte und ohne goto erntest Du da nichtmal eine gruene
Banane. Gut, ich muss gestehen, dass ich in meinen bisherigen C-Progs kein
einziges goto verwendete, dafuer aber massenweise breaks und continues und auch
da habe ich kein schlechtes Gewissen. ;-)
ES> Beides ist eigentlich schon Aufgabe der Design-Phase.
Am wichtigsten ist IMO das Ergebnis und die Effizienz (entgegen anderen
Behauptungen ;-) ) und nicht das Design. Wir sind doch nicht in der Modebranche
(oder habe ich den Zug schon verpasst?).
OG> Wie wuerdest Du das Problem denn loesen? Oder allgemein, wie beendest Du
OG> verschachtelte Schleifen?
Das hatten wir schon ein paarmal hier im Echo, deshalb ganz kurz:
z.B. durch Setzen eines Ende-Merkers, der in jeder Bedingung mit ner
Und-Verknuepfung durchgeschleift wird. Das ist auch das Ergebnis, was die
meisten CASE-Tools erzeugen duerften. Man kann aber auch durch die Anordnung
der Schleife etwas optimieren.
MM> Am wichtigsten ist IMO das Ergebnis und die Effizienz (entgegen anderen
MM> Behauptungen ;-) ) und nicht das Design. Wir sind doch nicht in der
MM> Modebranche (oder habe ich den Zug schon verpasst?).
Das Design ist aus mehreren Gruenden _wichtig_.
1) Es ist die einzige Methode, um mit Sicherheit auf eine funktionierende
Loesung zu kommen.
2) Es ist Bestandteil der Dokumentation. Und Code ohne Doku ist spaetestens bei
der Wartung wertlos.
3) Es hilft eben, solche Fehler zu vermeiden.
In professionellen Projekten gibt es z.B. durchaus bereits Tests mit dem
(teilweise) fertigen Design, die Aussagen ueber die Effektivitaet (nicht
Effizienz!) des Codes treffen koennen. Und wenn man da feststellt, dass das
Design Muell ist, wird neu gearbeitet, bevor man eine Zeile Code geschrieben
hat.
Uebrigens: Fehler, die man bei der Implementierung oder beim Test findet, sind
viel teurer als solche, die in der Analyse- oder Design-Phase gefunden werden.
Auch das ein Argument fuer das Design vor der Programmierung. In grossen
Projekten nehmen Analyse und Design deshalb auch 80% der Arbeitszeit ein. Nur
der Rest geht auf Implementierung und Einfuehrung.
MM> 1.) Stil ist Geschmacksache
Stil wird im Style-Guide des jeweiligen Projektes definiert.
MM> 3.) Weisst Du, wohin Du Dir Deine sogn. saubere
MM> Entwicklung und Deine vielgepriesenen A- und D-Tools stecken kannst? ;-)
MM> Wo sind bloss die ganzen Programmierer hin und wo kommen die ganzen
MM> Designer her? Erstere bitte wiederkommen, letztere bitte in's Modefach
MM> wechseln. ;-))
Gott sei Dank setzt sich die Erkenntnis langsam durch, dass das Design _vor_
dem Programm kommt. Anders kann man grosse Projekte eben nicht mehr handeln. Ob
man das mit Tools oder haendisch macht, ist zweitrangig. Wichtig ist, dass man
mit Methode vorgeht. Das geht auch mit Papier und Bleistift. Und auch dann
produzierst du keine GOTO's. Es sei denn, du benutzt z.B. die daemlichen
Ansi-Erweiterungen der Nassi-Shneidermann-Diagramme (die fuer break und
continue), die aber mit dem urspruenglichen Programm bzw. mit strukturierter
Entwicklung nichts mehr zu tun haben. Die kommen daher, dass einige Leute
meinten, man muesste jedes Stueck Code mit so nem Diagramm darstellen koennen.
Das ist aber nicht Sinn der Sache. Man kann jedes Problem mit so einem Diagramm
darstellen und man kann jedes korrekte Diagramm direkt in Code umsetzen. Dabei
werden aber diese Anweisungen nicht vorkommen.
MM> PS: 3.) bitte nicht missverstehen. :-)
nee, schon recht ;)
RH>> Einspruch. Sehr wohl machbar.
ES> Dann hast du schlechte Tools.
Das mache ich dir mit jedem Tool. Wenn man unbedingt will, kriegt
man sowas immer rein.
*** Am Donnerstag 01. August 1996 um 08:40 schrieb Matthias Thiele an Thomas
Seeling:
MT> Fuer den Fall while (!i && j) bzw while (i && j) wird
MT> auf alle modernen Compiler uebertragbar ist.
Na gut :-).
Die Kenntnis der deMorgan'schen Regeln ist aber nach wie vor sehr nuetzlich.
Ich bin mir sicher, dass hier schon ganz gut gespart werden kann.
MT> diesem Fall sagen: es ist ziemlich unelegant in einer Schleifenbedingung
MT> extra alles umzudrehen nur um den Compiler die Optimierung einer Negation
MT> zu ersparen :-).
Nicht umdrehen, sondern gleich so programmieren.
MM> Am wichtigsten ist IMO das Ergebnis und die Effizienz
MM> (entgegen anderen Behauptungen ;-) ) und nicht das Design.
So hart darf man das IMHO nicht darstellen. Ein gutes Design
senkt Erweiterungskosten und sichert damit Auftraege. Auch die
Einarbeitung neuer Mitarbeiter wird vereinfacht. Allerdings
straeube ich mich auch, das (krampfhafte) Design ueberzubewerten.
ES> Es geht immer definitiv vernuenftig ohne gotos. Nimm ne richtige
ES> Design-Methode (wenns nicht grad OO sein muss, z.B. SADT oder so was in
ES> der Richtung) und du hast das Problem nicht. Und gleichzeitig hast du
ES> schon mit der Analyse und dem Design die halbe Doku fuer deinen Code
ES> geschrieben, musst nur noch ein Funktions- und Variablenverzeichnis
ES> anhaengen (vorausgesetzt, es existiert ein Pflichtenheft).
du kommst augenscheinlich nicht ab und an in den genuss, wildfremde
uraltsourcen zu warten?
ansonsten: ich habe das programmieren in pascal gelernt und knapp 10 jahre nie
ein goto verwendet. mittlerweile seh ichs relaxter und setze in anderer leute
sourcen, wenn sie eh beschissen strukturiert sind, auch gnadenlos gotos ein.
aber wie gesagt: mit dem try-except-finally-labeling.
Ciao, Jan
--- n/a
* Origin: Und taeglich gruesst das kleine Arschloch (2:2476/552.42)
K>> Das ist ja dann auch genau der Grund, warum man in Turbo-Pascal
K>> Dinge wie short circuit evaluation, Schleifenabbrueche und so
K>> weiter als Sprachelemente eingefuehrt hat: Damit aus einer
K>> Wandtafelsprache eines Algorithmenprofessors ein Werkzeug wird,
K>> das im taeglichen Gebrauch ueberleben kann.
ES> Diese scheinbaren Verkuerzungen erzeugen nicht nur nach meiner Erfahrung
ES> in Wahrheit nur Verlaengerungen bei den Wartungsarbeiten. Sie wurden auch
ES> nicht eingefuehrt, weil sie notwendig sind, sondern weil
ES> einige goto-verliebte BASIC- und C-Programmierer nicht in der Lage waren,
ES> den Gedanken der strukturierten Programmierung richtig zu verstehen
ES> und umzusetzen.
aha. wie uebersetzt du folgendes konstrukt in seiner ganzen klarheit in
iso-pascal?
p = listenanfang;
while (p && p->key != searchedkey) {
p = p->next;
MM>>> Vorschlag: ein break; + Setzen eines Abbruchflags in der inneren
MM>>> und eine Abfrage dieses Abbruchflags in der aeusseren Schleife.
MM>>> Oder ein goto. :-) Tu Dir da nur keinen Zwang an. ;-)
ES>> Beides mieser Stil und _verboten_ fuer einen ordentlichen
ES>> Programmierer (und bei sauberer Entwicklung mittels A- und D-Tools
ES>> auch gar nicht machbar).
> 1.) Stil ist Geschmacksache
Es gibt IMHO Situationen, in denen die gezielte Verwendung eines goto die
Lesbarkeit und Wartbarkeit des Codes erhöht und in denen man zwanghafte
goto-Vermeidungskonstrukte nicht einsetzen sollte. Diese sind allerdings
relativ selten, z.B. beim Implementieren tail rekursiver Funktionen
(Beispiel auf Wunsch).
Andererseits können goto's sinnvoll sein, wenn der Sourcecode
maschinengeneriert und damit nicht von Hand zu warten ist, da dann die
einfache Codeerzeugung durch den x->C-Compiler im Vordergrund steht.
(Siehe z.B. Gambit-Scheme, Scheme->C etc.)
Insgesamt aber sollte der Einsatz dieses Sprachelementes auf solche
begründete Ausnahmesituationen beschränkt bleiben.
MfG
Ortwin
--- CrossPoint v3.11 R
* Origin: (2:2448/9020.48)
ES> Das Design ist aus mehreren Gruenden _wichtig_.
ES>
ES> 1) Es ist die einzige Methode, um mit Sicherheit auf eine
ES> funktionierende Loesung zu kommen.
Falsch. Verkehrte Analyse -> verkehrtes Design -> verkehrte
Software. CASE verhindert nicht, dass dein Projekt _mit_
_Sicherheit_ auf eine funktionierende Loesung kommt. Wie kommst
du bloss da drauf ? CASE funktioniert a la GIGO : garbage in,
garbage out. Eine Kreissaege garantiert auch nicht, dass du aus
einem Holzblock einen Tisch bekommst.
ES> 2) Es ist Bestandteil der Dokumentation. Und Code ohne Doku
ES> ist spaetestens bei der Wartung wertlos.
Wertlos nicht, aber sehr teuer.
ES> 3) Es hilft eben, solche Fehler zu vermeiden.
Nein. Siehe GIGO. Es kommt immer darauf an, _wer_ das Design
macht. Nicht das Desgin per se vermeidet Fehler.
ES>
ES> In professionellen Projekten gibt es z.B. durchaus bereits
ES> Tests mit dem (teilweise) fertigen Design, die Aussagen
ES> ueber die Effektivitaet (nicht Effizienz!) des Codes treffen
ES> koennen.
Wie designt ihr bloss ? Das Design legt mitnichten irgendwelchen
CODE fest. Kodieren kommt _nach_ dem Design, das ist naemlich die
Construction Phase.
ES> Und wenn man da feststellt, dass das Design Muell
ES> ist, wird neu gearbeitet, bevor man eine Zeile Code
ES> geschrieben hat.
Das ist das Prinzip der Meilensteine. Jede Phase bildet einen
Meilenstein. Bei Fehlern ist immer bis zum naechsten Meilenstein
zurueckzugehen.
ES> Uebrigens: Fehler, die man bei der Implementierung oder beim
ES> Test findet, sind viel teurer als solche, die in der
ES> Analyse- oder Design-Phase gefunden werden. Auch das ein
ES> Argument fuer das Design vor der Programmierung. In grossen
ES> Projekten nehmen Analyse und Design deshalb auch 80% der
ES> Arbeitszeit ein. Nur der Rest geht auf Implementierung und
ES> Einfuehrung.
Von der Sicht der Applikationserstellung scheint diese Aufteilung
korrekt. Bei Grossprojekten ist die Einteilung in Analyse, Design
und Implementierung jedoch viel zu grob, so daß auch die 80 &
nicht mehr gelten. Hinter einer Applikation haengt schliesslich
mehr als nur ein Sack von Programmen.
au revoir
Rainer
--- CrossPoint v3.11 R
Guten Tag,
meine eiserne Regel:
niemals irgendwas verkomplizieren, nur weils fuer den compiler
einfacher oder (scheinbar) effizienter ist, es sei denn,
es bringt wirklich einen signifikanten Gewinn (und das
ist wirklich aeuserst selten).
Also: immer so programmieren, dass mans lesen kann und der Quellkode
dann sauber strukturiert ist.
Ciao,
Marcus
>Erstens sind diese Konstruktionen uralt, zweitens haben sie mit Struktur
nichts
>zu tun und drittens sind genau das die Konstruktionen, die heute eine Wartung
>z.B. alter COBOL-Software nahezu unmoeglich und deshalb eine Neuentwicklung
>vieler Softwarepakete noetig machen.
Nun, Du hattest nach altem Code gefragt (eigentlich hattest Du
gefragt, was mit der Wartung von 20 Jahre altem Code ist und
ich hatte das zum Anlass genommen, ein 20 Jahre altes
PDP11-Band einzulesen und Dir ein wenig 20 Jahre alten Code zu
zitieren). So schwer zu warten koennen solche Konstrukte auch
nicht sein, denn in hoechst modernem und objektorientiert
strukturiertem Linux (ja, in C geschrieben) findest Du exakt
dieselben Ideome. Diese Redewendungen (Waechter-Returns und
Schleifenkurzschluesse, Exception-Handling mit goto's) muessen
die allgemeine Brauchbarkeitsrelation sehr gut erfuellen, wenn
sie sich so lange halten.
Und ein typischer Betriebssystemkernel ist auch nicht wirklich
ein kleines, leicht zu verstehendes Programm. Wenn ausgerechnet
*da* solche Konstruktionen massenhaft verwendet werden, dann
muessen sie etwas fuer sich haben. Oder, um
/usr/src/linux/Documentation/CodingStyle zu zitieren:
The answer to that is that if you need more than 3
levels of indentation, you're screwed anyway, and
should fix your program.
Die genannten goto's und goto-aehnlichen Konstrukte sind exakt
die Werkzeuge, mit denen man den indentation level seiner
Sourcen gering haelt und mit denen man den normal path of
execution straight haelt. Das hilft vor allen Dingen dem Lesen
und an einigen Stellen auch dem Compiler (das ist jedoch an den
meisten Stellen sekundaer).
>Die OOD-maessig hoechst umstritten ist und Quellcodes bereits heute schwer
>wartbar werden liess. Bis heute ist umstritten, ob Exceptions nun
>strukturmaessig sinnvoll oder gefaehrlich sind. Und nach dem, was ich bisher
>sah, welche Exceptions von manchen Leuten verbrochen werden, halte ich sie
eher
>fuer verboten.
Och, wenn Du auf dieser Tangente in die Umlaufbahn gehst, dann
solltest Du die Route auch zu Ende fliegen. Was ist mit if und
for? Das sind auch nur goto's im Abendkleid. Es waere
sicherlich viel interessanter, solche Strukturen auf zu
streichen und stattdessen ein typisiertes Lambda-Kalkuel
anzuwenden und if durch den y-Operator und for durch
endstaendige Rekursion zu ersetzen.
Das wuerde nebenbei auch noch so gefaehrliche Dinge wie
Seiteneffekte, Zeiger und konsequenterweise auch Variablen
generell abschaffen und waere eine grossartige Vereinfachung
beim Scheduling fuer symmetrische Multiprozessoren (da wir
keine Seiteneffekte mehr haben, koennen wir unabhaengige
Teilbaeume des Lambda-Ausdruckes leicht auf anderen Prozessoren
berechnen lassen). Und weil das beweistechnisch auch viel
einfacher zu handhaben ist, koennten wir den Pfad aus der
Designphase in die Codierungsphase auch weitergehender
automatisieren.
Dass das nicht einmal annaehernd realistisch ist, muessen
inzwischen sogar die Herren Langmaak und Kluge an der Uni Kiel
zugeben.
>Sie ist nicht nur vollstaendig, sondern auch sinnvoll. Es ist nachgewiesen,
>dass jedes mit "goto" und "unstrukturierten" Mitteln loesbare Problem auch
eine
>strukturierte Loesung hat, darueber gibts auch ne Doktorarbeit.
Korrekt. Und jede dieser goto-freien Loesungen ist direkt auf
eine Turing-Maschine uebertragbar, wie man leicht zeigen kann.
Die einzige Operation der Turing-Maschine ist jedoch das goto,
obendrein noch eines mit Seiteneffekten. So what?
>Im Gegenteil, alles andere ist unuebersichtlich und Muell. Ich weiss nicht, wo
>du programmierst, aber solcher Code wuerde bei mir in der Firma direkt in der
>Tonne landen. Gottseidank.
Ich arbeite zum Glueck nicht in einer Firma, in der
kaufmaennische C/S-Datenbankanwendungen erstellt werden
muessen. Ich hatte immer schon Schwierigkeiten damit, Programme
zu schreiben, deren Aufgabe die Summation endloser
Datenbankspalten und die abschliessende Multiplikation mit 1.15
ist. :-)
Ich arbeite bei einem Internet-Provider. Meine Aufgaben sind
Schulung, Beratung und Entwicklung, vorzugsweise im
Kernel/Geraetetreiber-Umfeld.
>ps: Mit deinem Absender scheint was nicht zu stimmen.
Nein, der ist in Ordnung. Es ist das Fido-Gateway und das
Datenformat von Fido, die nicht in Ordnung sind.
Kristian
--
Kristian Koehntopp, Wassilystrasse 30, 24113 Kiel, +49 431 688897
Sie haben sich auf der Grundlage harter Fakten eine feste Meinung
gebildet? Posten Sie sie doch einfach mal nach de.talk.bizarre.
Die kriegen dort beides wieder weich.
--- FIDOGATE 3.9.3
* Origin: Fido-Gateway @ white.schulung.netuse.de (2:240/2123.59)