> restart: # Prozeduren # (Modularisierung, Unterprogrammtechnik) # Erlaeuterung: # Eine haeufig wiederkehrende Aufgabenstellung wird ein fuer alle Male # programmiert, als Prozedur abgelegt, und kann dann beliebig oft als # Baustein in andere Programme (bzw. Sitzungen) eingebunden werden # # Man unterscheidet Definition und Aufruf der Prozedur # Zwischen Definition und Aufruf (Benutzung) kann ein beliebiger Zeitraum liegen # # Zunaechst einfache Funktionen (in BASIC oder FORTRAN heissen sie: function) # # Definition einer solchen Prozedur: > fu:=proc(x) > x^3-2 > end proc: # Aufruf dieser Prozedur mit Zahlen oder Variablen als Argumente: > fu(0),fu(3),fu(Pi); > fu(u); # Achtung: fu ist eine Funktion (Zuordnungsvorschrift), fu(u) ein Funktionsausdruck. > plot(fu(s),s=-3..3); # Graph des Funktionsausdrucks > plot(fu,-3..3); # einfacher: Graph der Funktion # # Bezeichnungen: fu ist der Name unserer Prozedur, der Wert der # Variablen fu ist die Prozedurdefinition. # In unserer Definition ist x der formale Parameter, beim Aufruf fu(3) # ist 3 der aktuelle Parameter. Der formale Parameter ist der Platzhalter # (dummy variable) fuer den erst beim Aufruf festzulegenden aktuellen Parameter. # Den Parameter x kann man als Eingabe der Prozedur deuten, den Wert des damit # gebildeten Ausdrucks x^3-2 als Ausgabe der Prozedur. # # Zum Vergleich: Eingebaute Funktionen sind ebenfalls Prozeduren > sin(3.), sin (Pi); sin(u); > plot(sin(s),s=-3..3); > plot(sin,-3..3); # Ansehen der Prozedurdefinition nicht so: > fu; # sondern so: > print(fu); # Ansehen der eingebauten Funktion sin nicht so: > print(sin); # sondern so: > interface(verboseproc=2): print(sin); # Stueckweise definierte Funktionen. Als Beispiel die Sprungfunktion: > spr:=proc(x) > if x<0 then 0 else 1 end if > end proc: > spr(3),spr(-3),spr(0); > plot(spr,axes=boxed); > spr(u); > plot(spr(s),s,axes=boxed); > spr(Pi); # Maple kann nicht entscheiden ob u<0, s<0 und sogar Pi<0 (?!) ist. # Abhilfe schaffen folgende Erweiterungen der Prozedur: > sprung:=proc(x) > if type(evalf(x),numeric) then > if evalf(x)<0 then 0 else 1 end if > else 'sprung(x)' end if > end proc: > sprung(3),sprung(-3),sprung(0); > plot(sprung,axes=boxed); > plot(sprung(s),s,axes=boxed); > sprung(Pi); > s:=sprung(u); > u:=7; s; # Verzoegerte Auswertung # Vereinfachung: Pfeilschreibweise > funeu:= x->x^3-2; > fu(u)-funeu(u); # Man erkennt dass die Funktionen fu und funeu identisch sind # # Eine Prozedur mit einer Liste von Zahlen als Eingabe, # Ausgabe soll der kleinste Wert dieser Zahlen sein. > restart: > mi:=proc(li) > kleinstwert:=li[1]; > for i from 2 to nops(li) do > if li[i] kleinstwert:=li[i] > end if > end do; > kleinstwert > end proc: # Ein Aufruf: > mi([6,5,-3,12,-7,1,-6]); # Ein Aufruf mit unzulaessigem Parameter liefert eine schwer zu # deutende Ausgabe > mi(27,19); # Abfrage der in der Prozedurdefinition benutzten (lokalen) Variablen > kleinstwert; i; # Die (lokalen) Variablen der Prozedur unterscheidet Maple von den # (globalen) Variablen der Sitzung auch dann, wenn sie vom Benutzer mit # genau demselben Namen belegt sind. # Maple warnt, man solle die lokalen Variablen explizit deklarieren. # Das machen wir in einer abgewandelten Prozedur hinter dem Schluesselwort local. # Ausserdem ergaenzen wir eine Typpruefung (hier auf Typ Liste (list)) hinter dem formalen Parameter. # Beim Aufruf mit einem aktuellen Parameter vom falschen Typ gibt Maple # dann eine Fehlermeldung > minimum:=proc(li::list) > local kleinstwert, i; > kleinstwert:=li[1]; > for i from 2 to nops(li) do > if li[i] kleinstwert:=li[i] > end if > end do; > kleinstwert > end proc: # > kleinstwert:=6000; > minimum([6,5,-3,12,-7,1,-6]); > kleinstwert; # Ausgegeben wird der Wert der globalen Variablen > minimum(27,19); # Der Prozeduraufruf liefert standardmaessig nur das Endergebnis. # Will man (z.B. zur Fehlersuche) auch Zwischenrechnungen einsehen, dann # kann man den Debugger einschalten wie folgt: > debug(minimum); > minimum([6,5,-3,12,-7,1,-6]); > undebug(minimum); # Natuerlich kann man den Parameter auch mit einem Namen belegen: > l:=[3,1,2]; > minimum(l); # # Babylonisches Wurzelziehen als Prozedur # Es wird nach dem Schluesselwort description ein Kommentar eingefuegt, # der beim Ausdruck der Prozedurdefinition mit print im Gegensatz zu # sonstigen Kommentaren wiedergegeben wird. # Wenn als aktueller Parameter eine negative Zahl eingegeben wird, dann # soll die Prozedur abbrechen mit einer Fehlermeldung, die hinter dem # Schluesselwort error angegeben ist. # Die Toleranz misst diesmal den relativen Fehler. # Die Eingabe a wird in eine Dezimalzahl verwandelt (sonst gaebe es z.B. # beim Argument Pi Probleme). Die Dezimalzahl darf aber nicht wieder # unter dem Namen a gespeichert werden, denn das wuerde den uebergebenen # Parameter in unzulaessiger Weise abaendern. Vielmehr wird fuer sie eine # lokale Variable adez bereitgestellt. # Um Endlosschleifen auszuschliessen, wird die Schleife nach 200000 # Durchlaeufen mit einer Fehlermeldung abgebrochen (Das geschieht z.B. beim # Aufruf wurzel(0), wo die Verwendung des relativen Fehlers nicht sachgerecht ist.) > restart: > wurzel:=proc(a) > local adez, x; > description "Quadratwurzel aus a mit der babyl. Methode"; > adez:=evalf(a); > if type(adez,numeric) then > if adez<0 then error "Wurzel nicht reell" end if; > x:=1; > to 200000 while abs(x^2-adez)>1.0E-9*adez do > x:=0.5*(x+adez/x); > end do; > if abs(x^2-adez)>1.0E-9*adez then > error "Keine Konvergenz" > else > x > end if > else > 'wurzel(a)' > end if > end proc: # > wurzel(64); > wurzel(Pi); > wurzel(I); > wurzel(-64); > w:=wurzel(s); > s:=81; w; > wurzel(1000000); > wurzel(100000); > wurzel(10^100); > wurzel(10^(-100)); > wurzel(10^(-100000)); > wurzel(0); > plot(wurzel); > print(wurzel); # # Guter Programmierstil: # -- Programmstrukturierung durch Prozeduren # -- Deklaration aller lokalen Variablen # -- Moeglichst keine globalen Variablen in Prozeduren verwenden # -- Moeglichst keine Veraenderung der uebergebenen Parameter # -- Kommentare # -- Typpruefung # -- Pruefung der Eingaben mit error-Abbruch # -- Ausschluss von Endlosschleifen #