Unterschied von Prozeduren und Funktionen

Früher, zu Zeiten von MS-DOS, da war die Welt noch in Ordnung. Funktionen (function) gaben grundsätzlich immer einen Wert zurück, Prozeduren (procedure) dagegen taten das niemals. Und die Rente war damals auch noch sicher. Nichts davon ist heute noch wahr.

Da es hier nicht um Politik geht beschränke ich mich auf den Unterschied von Prozeduren und Funktionen. Den gibt es nämlich bei den modernen dBASE-Versionen für Windows nicht mehr. Beide können einen Wert zurückliefern, müssen es aber nicht. Dazu ein Beispiel:

dMorgen = Heute() + 1
do Heute

Function Heute
private dDatum
dDatum = date()
? “Funktion! Heute ist der ” + dtoc(dDatum)
return (dDatum)

Procedure Heute
private dDatum
dDatum = date()
? “Prozedur! Heute ist der ” + dtoc(dDatum)
return

Hier haben wir eine Funktion namens heute und eine Prozedur mit demselben Namen! Das geht in diesem Fall sogar augenscheinlich gut, nur ist das Ergebnis vermutlich nicht das was Sie eigentlich erwarten würden. In der ersten Befehlszeile

dMorgen = Heute() + 1

rufen Sie function heute auf, denn heute() liefert ein Ergebnis (das aktuelle Systemdatum) zurück, also ein doch sehr typisches Verhalten für eine Funktion. Soweit so gut. Doch was passiert in der zweiten Befehlszeile

do Heute

Wird hier function heute oder procedure heute aufgerufen? Auf den ersten Blick würde man denken, dass hier die Prozedur aufgerufen wird, weil keine Rückgabe erwartet und im Code weiterverarbeitet wird, was typisch für einen Prozedur-Aufruf ist. Dem ist aber nicht so!

Auch in der zweiten Zeile wird function heute aufgerufen, die Textausgaben in den beiden Routinen beweisen es. Im Ausgabebereich des Befehlsfenster sehen Sie folgendes, wenn Sie obiges Programm als prg-Datei speichern und mit do … ausführen lassen:

Funktion! Heute ist der 28.11.2011
Funktion! Heute ist der 28.11.2011

Das ist eindeutig, in beiden Fällen wird die Funktion aufgerufen! Der Grund ist ebenso simpel wie erschreckend: es wird die Funktion aufgerufen, weil die im Quellcode zuerst steht, vor der Prozedur. dBASE ist im Laufe der Jahre leider etwas bequem geworden und nimmt in solchen Fällen die erstbeste Routine deren Name passt. Ob die ein Ergebnis zurückliefert oder nicht, bzw. ob ein solches Ergebnis in der aufrufenden Befehlszeile erwartet wird oder nicht, das ist dBASE inzwischen völlig egal.

Dass das jedoch alles andere als egal ist erkennen Sie dann, wenn Sie die Reihenfolge von function heute und procedure heute im Quellcode umdrehen. Das sieht dann so aus:

dMorgen = Heute() + 1
do Heute

Procedure Heute
private dDatum
dDatum = date()
? “Prozedur! Heute ist der ” + dtoc(dDatum)
return

Function Heute
private dDatum
dDatum = date()
? “Funktion! Heute ist der ” + dtoc(dDatum)
return (dDatum)

Wenn Sie dieses Programm mit dBASE Plus ausführen bekommen Sie … eine Fehlermeldung! Unzulässiger Datentyp in Zeile 1. Der Grund ist simpel: dBASE findet jetzt zuerst die Prozedur und verwendet sie, obwohl die ja kein Ergebnis zurückliefert. Und wo nichts ist kann auch nichts verwendet werden, geschweige denn dass man zu nichts auch noch 1 addieren könnte.

Bei der DOS-Version von dBASE war das noch anders. Dort funktionieren beide Beispiele und das Ergebnis ist in beiden Fällen korrekt, was die Ausgabe von z. B. dBASE III+ beweist:

Funktion! Heute ist der 28.11.2011
Prozedur! Heute ist der 28.11.2011

Die gute alte DOS-Version erkennt also, dass in der ersten Befehlszeile eine Funktion aufgerufen wird, weil ein Rückgabewert erwartet und weiterverarbeitet wird. Und in der zweiten Zeile wird kein Rückgabewert verwendet, also muss hier wohl die gleichnamige Prozedur gemeint sein, und die wird auch richtigerweise aufgerufen. Der Aufruf der Prozedur mit do … trägt auch seinen Teil dazu bei, diese korrekte Unterscheidung zwischen Funktion und Prozedur zu ermöglichen, jedenfalls bei den DOS-Versionen von dBASE.

Das ist eine böse Falle, vorallem für Umsteiger von DOS. Ich bekomme öfter Codes von alten DOS-Programmen und stolpere dort immer mal wieder auf gleiche Namen für Prozeduren und Funktionen. Kein Problem, früher ging das ja auch. Genauso wie die Rente früher sicher war (nebenbei, für Politiker ist sie das auch heute noch). Tia, und wenn Sie sowas dann aus alter Gewohnheit in eine aktuelle Windows-Version von dBASE übernehmen geht es plötzlich schief. Wenn Sie Glück haben bekommen Sie eine Fehlermeldung und wundern sich. Wenn Sie Pech haben ruft dBASE ohne dass Sie es merken einfach immer die erste der beiden Routinen auf, was womöglich zu Seiteneffekten und Folgefehlern führen kann, nach denen Sie lang suchen.

Daher zwei Ratschläge zur Verwendung von Prozeduren und Funktionen:

1. verwenden Sie niemals denselben Namen für eine function und eine procedure.

2. verwenden Sie immer eine function, wenn die Routine einen Wert zurückliefert, und verwenden Sie immer eine procedure wenn kein Ergebnis zurückgegeben wird.

Auch wenn 2. bei dBASE unter Windows nicht zwingend ist, so ist es doch eine grosse Hilfe, wenn Sie diesen Grundsatz konsequent beibehalten. Spätestens wenn Sie alte Programme nach vielen Jahren doch wieder überarbeiten wollen/müssen werden Sie froh darüber sein, wenn Ihr Code eine gewisse Struktur hat. Es erleichtert den Wiedereinstieg und hilft Ihrem Verständnis dafür “was um alles in der Welt Sie damals gemacht haben”. Ich kenne das …

Übrigens, in obigem Beispiel habe ich nur deshalb private für lokale Variablen verwendet, damit derselbe Code sowohl mit den aktuellen als auch mit den alten Versionen von dBASE lauffähig ist. Für die neuen Windows-Versionen empfiehlt sich für lokale Variablen natürlich immer der Geltungsbereich local, den die DOS-Versionen allerdings noch nicht kannten.

(Stand dieser Info: 2011)