Lazarus/Prozeduren, Funktionen und Bibliotheken und Lazarus/Die Unit LazIOStuff: Unterschied zwischen den Seiten

Aus ZUM-Unterrichten
< Lazarus(Unterschied zwischen Seiten)
K (Kasten blau/gelb -> blau/gelb)
 
main>Peterdauscher
Keine Bearbeitungszusammenfassung
 
Zeile 1: Zeile 1:
{{Lazarus-Buch}}
{{Lazarus-Buch}}
== Prozeduren ==


=== Einfache Prozeduren ===
== Wozu die Unit LazIOStuff ==


Wenn man Programme schreibt und immer mehr erweitert, so besteht die Gefahr, dass sie nicht nur immer größer sondern auch immer unübersichtlicher werden. Deshalb ist es sinnvoll, bestimmte, immer wiederkehrende Folgen von Befehlen mit einer bestimmten Bedeutung in einer Art selbst gebautem Befehl zusammenzufassen. Solche Befehle heißen auch '''Prozedur''' (engl.: '''procedure'''). Manche Leser werden den Sketch "Dinner for one" [https://de.wikipedia.org/wiki/Dinner_for_One] kennen, bei dem Butler James immer wieder fragt: "Same procedure as last year, Miss Sophie" und Miss Sophie jedes Mal antwortet "Same procedure as ''every'' year, James" . James und Miss Sophie meinen genau dasselbe wie wir: einen fest geregelten Ablauf von Dingen.
<tt>LazIOStuff</tt> ist eine Lazarus-Unit, die eine Reihe von Befehlen zur vereinfachten Ein- und Ausgabe zur Verfügung stellt. Man kann sich fragen, weshalb man überhaupt eine solche Unit schreibt, wo doch Lazarus von sich aus doch schon jede Menge Möglichkeiten zur Ein- und Ausgabe von Werten hat. Und weshalb man innerhalb eines Online-Lehrbuchs diese Unit benutzt.


Ein sehr sehr einfaches Beispiel für eine Prozedur könnte ein Hinweis sein, der angezeigt werden soll, begleitet von einem hörbaren Signal. Das Programm benötigt lediglich einen Button mit dem Namen <tt>Button1</tt>.
Der Hauptgrund besteht darin, dass es beim Lernen einer Programmiersprache angenehm ist, wenn man sich auf die wesentlichen Dinge konzentrieren kann. Vereinfachte Befehle für Ein- und Ausgabe sollen genau dabei helfen. So haben wir z.B. in den vergangenen Kapiteln gesehen, dass wir immer wieder den Inhalt von Edit-Feldern zu Integer- oder Real-Zahlen übersetzen mussten. In den folgenden Kapiteln wollen wir Ergebnisse in Memo-Felder ausgeben, das sind Komponenten mit mehreren Textzeilen.  
Das alles würde auch relativ leicht in Lazarus direkt funktionieren, jedoch würde der Programmcode etwas unübersichtlich, und das kann dem Lernen dann doch abträglich sein.


{{blau|<source  line highlight="30-34,38" lang="pascal">
== Download ==
unit hinweis_geben;
Die zugehörige Datei kann hier heruntergeladen werden [http://sourceforge.net/projects/laziostuff/files/laziostuff.pas/download laziostuff.pas]. Die Pascal-Datei muss in das Verzeichnis kopiert werden, in der sich das Lazarus-Projekt befindet, in das es eingebunden werden soll. Das Einbinden selbst erfolgt durch Hinzufügen von <tt>LazIOStuff</tt> in die Zeile <tt>uses ...</tt>


== Die von LazIOStuff bereitgestellten Befehle ==
{| class ="wikitable toptextcells"
! Befehl
! Bedeutung
|-
| WriteTo(Komponente, Wert, [Gesamtanzahl Zeichen, Nachkommastellen]) || Schreibt einen Wert (vom Typ String, Integer oder Double) in eine Komponente.<br /> Das kann entweder eine Edit-Komponente oder eine mehrzeilige Memo-Komponente sein. <br />Bei Edit-Komponenten wird der darin befindliche Inhalt einfach ersetzt, bei Memo-Komponenten jedoch hinter den bisherigen Inhalt angehängt. <br/> Bei Integer-Werten kann zusätzlich angegeben werden, wie viele Zeichen die Ausgabe insgesamt haben soll (rechtsbündige Zahlen). <br/>  Bei Double-Zahlen kann man zusätzlich dazu wiederum noch einstellen, mit wie vielen Nachkommastellen die Zahl angezeigt wird.
|-
| WriteLnTo(Komponente, Wert, [Gesamtanzahl Zeichen, Nachkommastellen]) || Funktioniert ähnlich wie WriteTo, mit einem wesentlichen Unterschied: <br/> Diese Funktion bezieht sich ausschließlich auf die mehrzeiligen Memo-Komponenten. <br/>Nach der Ausgabe verursacht sie gezielt einen Zeilenumbruch.<br/> WriteLnTo kann man auch ganz ohne Klammern verwenden; dann erzeugt es einfach nur einen Zeilenumbruch.
|-
| ReadFrom(Edit-Komponente, Wert) || Liest aus einer Edit-Komponente einen Wert (entsprechend des Datentyps) aus. Möglich sind Zeichenketten (Strings), Integer- und Double-Werte.
|-
| Clear(Komponente) ||Löscht den Inhalt der entsprechenden Komponente.
|-
|}
Das folgende Beispiel demonstriert die verschiedenen Befehle der Unit LazIOStuff und benötigt die Komponenten <tt>Button1, Edit1 und Memo1</tt>.
{{kasten_blau|<source  line highlight="6,30-40" lang="pascal">
unit lazio_testumgebung;
{$mode objfpc}{$H+}
{$mode objfpc}{$H+}
interface
interface
uses
uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, LazIOStuff;
  ExtCtrls;


type
type
Zeile 22: Zeile 43:
   TForm1 = class(TForm)
   TForm1 = class(TForm)
     Button1: TButton;
     Button1: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
     procedure Button1Click(Sender: TObject);
     procedure Button1Click(Sender: TObject);
   private
   private
Zeile 35: Zeile 58:


{$R *.lfm}
{$R *.lfm}
{ TForm1 }
{ TForm1 }
procedure Hinweis;
begin
  Beep;
  ShowMessage('Sie haben den Knopf gedrückt');
end;


procedure TForm1.Button1Click(Sender: TObject);
procedure TForm1.Button1Click(Sender: TObject);
var zahl : integer;
begin
begin
Hinweis;
ReadFrom(Edit1,zahl);
Clear(Memo1);
WriteLnTo(Memo1,'Es geht los!');
WriteLnTo(Memo1,zahl);
WriteLnTo(Memo1,zahl,10);
WriteLnTo(Memo1,' wurde eingegeben. Und die Hälfte davon ist:');
WriteLnTo(Memo1,zahl/2,20,5);
end;
end;


end.
end.
</source>}}
Man sieht, dass es nach der Definition des eigenen Befehls (der Prozedur) "Hinweis" vollkommen genügt, diesen Befehl aufzurufen.
=== Prozeduren mit Wertparametern ===
Die oben gezeigte Prozedur tut immer genau das gleiche: Sie gibt das akustische Signal und den Hinweis "Sie haben den Knopf gedrückt". Häufig jedoch kommt es vor, das eine Prozedur zwar immer ähnliche, aber eben doch leicht verschiedene Dinge tun soll. So wäre es z.B. praktisch, wenn man den Hinweistext selbst immer wieder neu bestimmen könnte. Das funktioniert auch, wenn man der Prozedur einen so genannten Paramter mit auf den Weg gibt. Wir betrachten wiederum ein Beispiel:
{{blau|<source line start="30" highlight="1,9" lang="pascal">
procedure Hinweis(nachricht: string);
begin
  Beep;
  ShowMessage(nachricht);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Hinweis('Eine frei gewählte Nachricht');
end;
</source>}}
</source>}}


Unterscheiden tun sich die gelb markierten Zeilen: Bei der Definition der Prozedur "Hinweis" in Zeile 30 ist hinter dem Namen der Prozedur noch in Klammern eine Variable angegeben, die in diesem Fall den Namen <tt>nachricht </tt> trägt und vom Typ <tt>string</tt> ist.
Die Ausgabe in der <tt>Memo1</tt>-Komponente nach Eingabe der Zahl "7" in der Komponente <tt>Edit1</tt> ist:
Wird nun in Zeile 40 der neue Befehl <tt>Hinweis</tt> aufgerufen, und zwar mit der Zeichenkette <tt>'Eine frei gewählte Nachricht'</tt> in Klammern, so wird die Variable <tt>nachricht</tt> mit diesem Wert belegt.
{{kasten_blau|<source lang="pascal">Es geht los!
Wird nun innerhalb der Prozedur wiederum der Befehl ShowMessage mit <tt>nachricht</tt> als Parameter aufgerufen, so erscheint tatsächlich die Meldung mit dem richtigen Text auf dem Bildschirm.
7
 
        7
'''Wichtige Anmerkung:'''
  wurde eingegeben. Und die Hälfte davon ist:
Die Variable <tt>nachricht</tt> ist eine neue, eigenständige Variable. Beim Aufruf der Prozedur Hinweis wird ihr ein entsprechender Zeichenkettenwert zugewiesen. Wird dieser im Lauf der Prozedur verändert, so wirkt sich das auf das aufrufende Programm nicht aus.
            3,50000
 
=== Prozeduren mit Referenzparametern ===
 
Nun kann es sein, dass es der Zweck einer Prozedur sein soll, den Wert einer Variable im aufrufenden Programm aber tatsächlich zu ändern. Als Beispiel betrachten wir den (sehr sehr einfachen) Fall, bei dem der Wert einer Integer-Variable verdoppelt werden soll. Das Programm benötigt den Button <tt>Button1</tt> und das Edit-Feld <tt>Edit1</tt>. Der Vorspann mit Variablen usw. ist hier einfach einmal weggelassen; aber es ist klar, dass dort <tt> zahlenwert : integer;</tt> deklariert werden muss.
 
{{blau|<source line start="30" highlight="1,10" lang="pascal">
procedure verdopple (zahl : integer);
begin
zahl:=zahl*2;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
zahlenwert:=StrToInt(Edit1.Text);
verdopple(zahlenwert);
Edit1.Text:=IntToStr(zahlenwert);
end;
</source>}}
 
Beim Ausprobieren merkt man: Das Beispiel funktioniert offensichtlich nicht. Der Inhalt des Edit-Feldes bleibt beim Drücken des Knopfes genau gleich.
 
Wir müssen das Programm leicht abändern, damit es funktioniert. Wir fügen in Zeile 30 vor der Variable <tt>zahl</tt> das Schlüsselwort <tt>var</tt> ein. Das ist eigentlich ein wenig missverständlich, denn es soll bedeuten:
<tt>zahl</tt> ist '''keine''' eigenständige Variable sondern nur noch eine Art Spitzname für die Variable im Funktionsaufruf (man sagt auch: '''Referenz auf die Variable'''), in diesem Fall <tt>zahlenwert</tt>. Mit
<tt>zahl:=zahl*2</tt> wird jetzt in Wirklichkeit die Variable <tt>zahlenwert</tt> verdoppelt.
 
{{blau|<source line start="30" highlight="1,9" lang="pascal">
procedure verdopple (var zahl : integer);
begin
zahl:=zahl*2;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
zahlenwert:=StrToInt(Edit1.Text);
verdopple(zahlenwert);
Edit1.Text:=IntToStr(zahlenwert);
end;
</source>}}
 
Durch diese kleine Änderung funktioniert jetzt das Programm tadellos.
 
== Funktionen ==
 
Im Grunde würden Prozeduren mit Wert- und Referenzparametern vollkommen ausreichern, um immer wiederkehrende Abläufe in selbstdefinierte Befehle zu verpacken (Informatiker sprechen gerne auch von "'''verkapseln'''").
 
So könnte man zum Beispiel eine Prozedur zum Berechnen des Quadrats von integer-Zahlen schreiben. Das Beispielprogramm benötigt die Komponenten <tt>Button1, Edit1, Edit2</tt> und natürlich auch die Integer-Variablen <tt>zahlenwert</tt> und <tt>zahlenwertquadriert</tt>.
 
 
{{blau|<source line start="30" highlight="1,9" lang="pascal">
procedure quadrat(zahl : integer; var ergebnis: integer);
begin
ergebnis:=zahl*zahl;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
zahlenwert:=StrToInt(Edit1.Text);
quadrat(zahlenwert,zahlenwertquadriert);
Edit2.Text:=IntToStr(zahlenwertquadriert);
end;                           
</source>}}
 
Aber gerade im mathematischen Bereich ist das eher unüblich: außerdem muss man immer überlegen, welcher der beiden Parameter nun die eigentliche Zahl und welche das spätere Quadrat werden soll. Eleganter ist daher die folgende Schreibweise:
 
{{blau|<source line start="30" highlight="1,9" lang="pascal">
function quadrat(zahl : integer) : integer;
begin
quadrat:=zahl*zahl;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
zahlenwert:=StrToInt(Edit1.Text);
zahlenwertquadriert:=quadrat(zahlenwert);
Edit2.Text:=IntToStr(zahlenwertquadriert);
end;                           
</source>}}
 
Beim Aufruf der Funktion erhält sie durch die Berechnung einen Funktionswert. Dies geschieht in Zeile 32.
 
In Zeile 38 wird dieser Funktionswert der Variablen "zahlenwertquadriert" zugewiesen. Eine solche Zuweisung über ein ":=" funktioniert nur bei Funktionen, nicht bei Prozeduren.
 
Anmerkung:
Alternativ kann man die Zeile 32 auch so schreiben:
{{blau|<source line start="32" highlight="32" lang="pascal">
result:=zahl*zahl;
</source>}}
</source>}}
Der Variablenname "result" ist genau für diesen Zweck reserviert; man sollte ihn also nicht für andere Dinge verwenden.


== Bibliotheken ==
Das Ausgliedern von immer gleichen Routinen in Prozeduren und Funktionen als eigene Befehle macht auch größere Programme übersichtlicher. Allerdings hilft bei sehr großen Programmen (mit mehreren Tausenden Zeilen) das auch nicht mehr so sonderlich viel. Deshalb kann man in diesem Fall Teile der Prozeduren und Funktionen in eigene Dateien ausladen, so genannte Bibliotheken, die in der Sprache Pascal auch als <tt>Units</tt> bezeichnet werden. 


Ein weiterer Vorteil von Bibliotheken ist, dass mehrere andere Programme die darin definierten Befehle benutzten Befehle verwenden können, ohne dass man die entsprechenden Funktionen und Prozeduren in das eigene Programm hineinkopieren muss. Vielmehr genügt es, in der Zeile <tt>uses ...</tt> den Namen der benutzten Unit anzugeben.
Eine solche Bibliothek haben wir im Kapitel [[../Computer-Mathematik/]] bereits kennen gelernt: die <tt>Unit Math</tt>, in der viele praktische mathematische Befehle definiert sind.


== Aufgaben ==
{{Lazarus-Buch}}
#Schreibe eine Prozedur, bei der Fläche und Umfang eines Rechtecks über Referenzparameter zurückgegeben werden. Länge und Breite sollen über Wertparameter in die Prozedur importiert werden. Teste die Prozedur in einem Programm.
#Schreibe zwei Funktionen, die für ein Rechteck mit gegebener Länge und Breite die Fläche bzw. den Umfang ausrechnen und teste sie in einem Programm.
#Vergleiche die letzten beiden Aufgaben: Nenne Vor- und Nachteile der beiden Lösungen für das gleiche Problem.
#Informiere Dich im Internet (oder Mathematikbuch) über den Höhensatz und den Kathetensatz. Kannst Du eine Reihe von sinnvollen Prozeduren bzw. Funktionen schreiben?
#Die Betragsfunktion in der Mathematik ist folgendermaßen definiert: Wenn eine Variable x einen Wert>=0 hat, so ist ihr Betrag genauso groß wie ihr Wert x. Hat sie dagegen einen negativen Wert, so ist der Betrag gerade -x. Konstruiere eine sinnvolle Funktion <tt>betrag(x)</tt>.

Version vom 14. Januar 2013, 20:21 Uhr


Wozu die Unit LazIOStuff

LazIOStuff ist eine Lazarus-Unit, die eine Reihe von Befehlen zur vereinfachten Ein- und Ausgabe zur Verfügung stellt. Man kann sich fragen, weshalb man überhaupt eine solche Unit schreibt, wo doch Lazarus von sich aus doch schon jede Menge Möglichkeiten zur Ein- und Ausgabe von Werten hat. Und weshalb man innerhalb eines Online-Lehrbuchs diese Unit benutzt.

Der Hauptgrund besteht darin, dass es beim Lernen einer Programmiersprache angenehm ist, wenn man sich auf die wesentlichen Dinge konzentrieren kann. Vereinfachte Befehle für Ein- und Ausgabe sollen genau dabei helfen. So haben wir z.B. in den vergangenen Kapiteln gesehen, dass wir immer wieder den Inhalt von Edit-Feldern zu Integer- oder Real-Zahlen übersetzen mussten. In den folgenden Kapiteln wollen wir Ergebnisse in Memo-Felder ausgeben, das sind Komponenten mit mehreren Textzeilen. Das alles würde auch relativ leicht in Lazarus direkt funktionieren, jedoch würde der Programmcode etwas unübersichtlich, und das kann dem Lernen dann doch abträglich sein.

Download

Die zugehörige Datei kann hier heruntergeladen werden laziostuff.pas. Die Pascal-Datei muss in das Verzeichnis kopiert werden, in der sich das Lazarus-Projekt befindet, in das es eingebunden werden soll. Das Einbinden selbst erfolgt durch Hinzufügen von LazIOStuff in die Zeile uses ...


Die von LazIOStuff bereitgestellten Befehle

Befehl Bedeutung
WriteTo(Komponente, Wert, [Gesamtanzahl Zeichen, Nachkommastellen]) Schreibt einen Wert (vom Typ String, Integer oder Double) in eine Komponente.
Das kann entweder eine Edit-Komponente oder eine mehrzeilige Memo-Komponente sein.
Bei Edit-Komponenten wird der darin befindliche Inhalt einfach ersetzt, bei Memo-Komponenten jedoch hinter den bisherigen Inhalt angehängt.
Bei Integer-Werten kann zusätzlich angegeben werden, wie viele Zeichen die Ausgabe insgesamt haben soll (rechtsbündige Zahlen).
Bei Double-Zahlen kann man zusätzlich dazu wiederum noch einstellen, mit wie vielen Nachkommastellen die Zahl angezeigt wird.
WriteLnTo(Komponente, Wert, [Gesamtanzahl Zeichen, Nachkommastellen]) Funktioniert ähnlich wie WriteTo, mit einem wesentlichen Unterschied:
Diese Funktion bezieht sich ausschließlich auf die mehrzeiligen Memo-Komponenten.
Nach der Ausgabe verursacht sie gezielt einen Zeilenumbruch.
WriteLnTo kann man auch ganz ohne Klammern verwenden; dann erzeugt es einfach nur einen Zeilenumbruch.
ReadFrom(Edit-Komponente, Wert) Liest aus einer Edit-Komponente einen Wert (entsprechend des Datentyps) aus. Möglich sind Zeichenketten (Strings), Integer- und Double-Werte.
Clear(Komponente) Löscht den Inhalt der entsprechenden Komponente.

Das folgende Beispiel demonstriert die verschiedenen Befehle der Unit LazIOStuff und benötigt die Komponenten Button1, Edit1 und Memo1.

Vorlage:Kasten blau

Die Ausgabe in der Memo1-Komponente nach Eingabe der Zahl "7" in der Komponente Edit1 ist: Vorlage:Kasten blau