7 Werte- und Variablen-Parameter:

Zum vorigen Kapitel Zum Inhaltsverzeichnis Zum nächsten Kapitel



Wenn einer Prozedur, die als
    Procedure tuwas (a: Integer);
deklariert ist, ein Parameter a übergeben wird, dann kann beim Aufruf der Prozedur entweder ein numerischer Integer-Wert, eine Variable oder ein Term übergeben werden. Es sind dann
    tuwas(3) 
    tuwas(b) {wobei b eine Integer-Variable sein muß}
    tuwas(3*b-5)
drei verschiedene, aber gültige Aufrufe für diese Prozedur. Wenn nun innerhalb der Prozedur der Wert des internen Parameters a verändert wird, dann hat dies keine Auswirkungen auf den Wert von b außerhalb der Prozedur, selbst wenn diese mit tuwas(b) aufgerufen wurde: die Variable b behält stets den Wert, den sie vor dem Prozedur-Aufruf hatte. Dies liegt daran, dass beim Aufruf der Prozedur der Wert des als Parameter angegebenen Terms berechnet wird und dann nur dieser Wert in der lokalen Variablen a gespeichert wird. Nach dem Verlassen der Prozedur ist die Variable a verloren. Wegen dieses Verhaltens nennt man solche Prozedur-Parameter Werte-Parameter: der Prozedur wird nur der Wert der Variablen übergeben, nicht die Variable selbst!

Wie muss man es nun anstellen, damit eine Prozedur wirklich Zugriff auf die übergebene Variable hat und damit auch Informationen ans aufrufende Programm zurückgeben kann? Es muss dafür gesorgt werden, dass in der Prozedur statt mit der lokalen Varibalen a eben doch mit der übergebenen Variablen b gearbeitet wird! Dies erreicht man, indem man den Parameter als Variablen-Parameter deklariert:
    procedure tuwas (var a: Integer);
Dann jedoch muss beim Aufruf der Prozedur stets eine Variable vom passenden Typ als Parameter übergeben werden. Wird nun die Prozedur mit
    tuwas(b)
aufgerufen, dann werden alle Veränderungen, die die Prozedur zur Laufzeit an der Variablen a vornimmt, tatsächlich an der übergebenen Variablen b vorgenommen: "a" ist also dann nur ein anderer Name für die Variable b, sozusagen ein Alias!

Merken Sie sich also:
Will man Werte an eine Prozedur übergeben, verwendet man Werteparameter;
soll die Prozedur äußere Variablen verändern, verwendet man Variablenparameter.
Wenn man in der Parameterliste einer Prozedur sowohl Werte- als auch Variablenparameter braucht, dann werden die Deklarationen durch Strichpunkte voneinander getrennt, z.B.:
    procedure tunochwas(a, b: Integer; c: Double; var d: Double; var e, f : Integer);
Es ist sinnvoll, zuerst alle Werteparameter und dann alle Variablenparameter aufzuführen. Können Sie sich denken, warum manche Programmierer auch von "Input-" und "Output-Parametern" reden?




  1. Das Tausch-Problem:

    Fritzchen Pfiffig soll ein Programm schreiben, bei dem auf Knopfdruck die Inhalte von zwei IntEdit-Feldern ausgetauscht werden. Er benutzt dazu sein neu erworbenes Wissen über Prozeduren. Hier ein paar Fragmente aus seinem Programm:
          procedure TForm1.tausche(x, y: Integer);
            begin
            x := y;
            y := x;
            end;
    
          procedure TForm1.Button1Click(Sender: TObject);
            var a, b: Integer;
            begin
            a := IntEdit1.Value;
            b := IntEdit2.Value;
            tausche(a, b);
            IntEdit1.Value := a;
            IntEdit2.Value := b;
            end;
    
    1. Schreiben Sie ein solches Programm, und übernehmen Sie die obigen Programmteile zunächst ohne Änderungen. Sie sind zu faul zum Tippen? Also gut, hier gibt's Fritzchen Pfiffig's Tausch-Programm zum Download!

    2. Leider zeigt das Programm beim Klick auf den Knopf keinerlei Reaktion. Wissen Sie, warum? Wer sich bei Werte- und Variablenparameter auskennt, sollte den Notstand beheben können!

    3. Selbst wenn Sie nun eine Reaktion bekommen, ist es wahrscheinlich noch nicht die richtige, weil die Prozedur "tausche" noch einen logischen Fehler enthält. Überzeugen Sie sich davon, dass bei Fritzchen Pfiffig's Entwurf der Inhalt von x unwiederbringlich verloren geht! Um das zu verhindern, müssen Sie zuvor den Inhalt von x in einer dritten Variablen "aufbewahren" und dann die zweite der obigen Zuweisungen entsprechend ändern!
    [Lösungsvorschlag]



  2. Das Bruch-Problem:

    Schreiben Sie ein Programm "KEB" (d.h. "KürzeEinenBruch"), das 4 passende Edit-Komponenten und einen Knopf enthält. Die Edit-Komponenten sollen Zähler und Nenner von 2 Brüchen darstellen. Auf Knopfdruck soll ein in den ersten beiden Editfeldern stehender Bruch gekürzt in den anderen beiden Editfeldern erscheinen.

    Schreiben Sie dazu eine Prozedur "kuerze", der sie Zähler z und Nenner n des zu kürzenden Bruchs übergeben; die Prozedur soll dann die gekürzten Werte zurückliefern. Der gekürzte Bruch soll dann mit Hilfe des dritten und vierten Editfeldes dargestellt werden. Beachten Sie, dass Zähler und Nenner stets ganze Zahlen sind, in Delphi also durch Integer-Zahlen dargestellt werden müssen.

    Wie kürzt man eigentlich einen Bruch? Indem man Zähler z und Nenner n so lange durch ihre gemeinsame Faktoren teilt, bis sie eben keine gemeinsamen Faktoren mehr haben. Die folgende Tabelle stellt das Vorgehen am Beispiel des Bruches 168/180 dar:

    Schritt   z     n     t   t teilt z t teilt n z DIV t n DIV t Aktion
    1. 168 180 2 ja ja 84 90 Kürzen mit t=2
    2. 84 90 2 ja ja 42 45 Kürzen mit t=2
    3. 42 45 2 ja nein -- -- t erhöhen
    4. 42 45 3 ja ja 14 15 Kürzen mit t=3
    5. 14 15 3 nein ja -- -- t erhöhen
    6. 14 15 4 nein nein -- -- t erhöhen
    7. 14 15 5 nein ja -- -- t erhöhen
    8. 14 15 6 nein nein -- -- t erhöhen
    9. 14 15 7 ja nein -- -- t erhöhen
    10. 14 15 8 nein nein -- -- t erhöhen
    ... ... ... ... ... ... ... ... ...
    16. 14 15 14 ja nein -- -- Ende

    Zugegeben, niemand wird ernsthaft den Teiler 4 oder 8 probieren, wenn er schon zuvor sichergestellt hat, dass Zähler und Nenner keinen Primfaktor 2 mehr enthalten. Und eigentlich ist Ihnen ja schon nach dem 5. Schritt klar, dass 14 und 15 teilerfremd sind, und die Suche daher abgebrochen werden kann. Das sture Vorgehen, alle Teiler t zu testen, die nicht größer als die kleinere der beiden Zahlen z und n ist, hat aber den Vorteil, einerseits korrekt (d.h.: nicht falsch!) und andererseits leicht programmierbar zu sein! Im Zeitalter der Gigahertz-Prozessoren können wir die paar überflüssigen Rechenschritte in Kauf nehmen und zunächst darauf verzichten, den Algorithmus effizienter zu machen.

    Ob t ein Teiler von (z.B.) z ist, testen wir mit der MOD-Verknüpfung:
      t teilt genau dann z ohne Rest, wenn z MOD t = 0 ist.

    Damit läßt sich der Algorithmus für die Prozedur "kuerze" also folgendermaßen formulieren:
        Setze t auf 2;
        Solange (t <= z) und (t <= n) sind, wiederhole:
           Wenn (z MOD t = 0) und (n MOD t = 0) sind, dann
              teile z durch t
              teile n durch t
           andernfalls
              erhöhe t um 1.
    
    Für das Teilen müssen Sie den Ganzzahl-Operator "DIV" benutzen, und nicht den "/"! (Sonst ist das Ergebnis nämlich keine Integerzahl mehr.)

    Mit diesen Hinweisen sollte das KEB-Programm nun ein für Sie bewältigbares Problem darstellen! Damit Sie hinreichend motiviert sind, es auch wirklich ernsthaft anzugehen, gibt es diesmal keinen Lösungsvorschlag.




  3. Luxus-Version für Mathematiker

    Wer sich doch daran stört, dass das Programm aus der vorigen Aufgabe viel unnütze Arbeit macht, der kann eine mathematisch anspruchsvollere Variante "KEB2" schreiben, die sehr viel effizienter ist:

    Um einen Bruch vollständig zu kürzen, muss man Zähler z und Nenner n durch deren größten gemeinsamen Teiler (ggT) dividieren. Eine elegante und vor allem schnelle Methode zur Berechnung des ggT hat EUKLID mit seinem berühmten "Euklidischen Algorithmus" schon vor über 2000 Jahren angegeben:
       Es sei a die größere der beiden Zahlen a und b.
       Solange nun b größer als 0 ist, wiederhole folgendes:
           berechne den Rest r, den a bei der Division durch b läßt;
           weise a den Wert von b zu;
           weise b den Wert von r zu.
       Danach ist a der gesuchte ggT.
    
    Gehen Sie nun folgendermaßen vor:

    1. Deklarieren Sie die Prozedur "ggT" im "private"-Abschnitt ihrer Formular-Deklaration. Sie soll 3 Parameter vom Typ Integer erhalten: in den ersten beiden sollen die Werte von Zähler und Nenner des zu kürzenden Bruchs übergeben werden, im dritten Parameter soll die Prozedur den ggT dieser beiden Zahlen zurückgeben.

    2. Zeichnen Sie sich ein Struktogramm für den Euklidischen Algorithmus auf, und spielen Sie den Algorithmus auf dem Papier durch für die Zahlenpaare (560, 91) und (972, 666).

    3. Implementieren Sie die ggT-Prozedur gemäß diesem Algorithmus! Eigentlich ist dazu nicht viel mehr nötig als eine schlichte WHILE-Schleife.
        Möglicherweise kommt Ihnen aber schon die erste Zeile mysteriös vor: "Es sei a die größere der beiden Zahlen." Für a=24 und b=15 ist das ja okay, denn dann ist wirklich a die größere der beiden Zahlen! Was aber ist zu tun, wenn umgekehrt a=15 und b=24 ist? Nun, dann müssen Sie eben die Inhalte der Variablen a und b vertauschen! Wie man das macht, haben Sie ja schon in der obigen Aufgabe 1 gelernt... ;-)

    4. Stellen Sie sicher, dass Sie bei der Deklaration der ggT-Prozedur Werte- und Variablenparameter sinnvoll und zielführend eingesetzt haben!

    5. Vervollständigen Sie das Programm und testen Sie es!

    Achten Sie auch darauf, dass Ihr "KEB2"-Programm keine Nenner mit dem Wert Null zuläßt! Beim Programmieren einer Reaktion auf diesen Krisenfall hilft die Delphi-Prozedur "ShowMessage()": sie zeigt einen als Parameter übergebenen Text in einem kleinen Fenster an, welches der Benutzer durch einen Klick auf einen Okay-Button schließen muss, um weiterarbeiten zu können. Damit können Sie Ihrem Benutzer zur Laufzeit eine zusätzliche Information zukommen lassen.

    [Lösungsvorschlag]




  4. Bruch-Rechnen:

    Mit dem, was Sie im obigen Beispiel gelernt haben, läßt sich leicht ein Programm zur Bruchrechnung schreiben, das 3 Brüche entgegennimmt und überprüft, ob der 3. Bruch die Summe der beiden ersten ist. Geben Sie die Reaktion des Programms in einem Message-Fenster aus (z.B. mit "ShowMessage" oder auch dem komplizierteren "MessageDlg", Details siehe Delphi-Hilfe!). Dabei sollte das Programm auch den Fall erkennen und entsprechend kommentieren, dass das Ergebnis zwar richtig, aber nicht vollständig gekürzt ist.

    Sie können die "Aufgaben" (d.h.: die Belegung der beiden ersten Brüche) auch durch die RANDOM()-Funktion automatisch generieren lassen. Aber Vorsicht: nur kleine positive ganze Zahlen verwenden, sonst wird's zu schwer!

    [Lösungsvorschlag]

    (So etwa beginnt die Entwicklung eines Lernprogramms.....)



Zum vorigen Kapitel Zum Inhaltsverzeichnis Zum nächsten Kapitel