
| 1. |
Einleitung | warum,
weshalb und wieso =? |
|
| 2. |
Variablen | Standardtypen |
|
| 3. |
Konstanten | genau
das |
|
| 4. |
Typen | eigene
Variablen - Typen |
|
| 5. |
Records | zusammengesetzte
Variablen |
|
| 6. |
Zeiger | dynamisches
Arbeiten mit Variablen |
|
| 7. |
Schleifen | REPEAT, FOR, WHILE ect. | |
| 8. |
Bedingungen | IF-THEN,
CASE ect. |
|
| 9. |
Prozeduren | mit,
ohne Paramter und oder Rückgabewert |
|
| 10. |
Module | eigene
Bibliotheken, Auslagerung von Funktionen |
|
| 11. |
Objekte | ein
Versuch =/ |
|
| 12. |
Threads |
sowas
geht immerhin auch =) |
|
| 13. |
Ausnahmen | Exceptions
- fangen und werfen =) |
|
| 14. |
Befehls-Referenz | Übersicht
über Funktionen der einzelnen Bibliotheken (Module) |
|
| 15. |
Download |
alle Beispiele gepackt zum download |
|
| 16. |
Links |
Ein
paar nützliche Adressen =) |
IO.Put("Hello World\n");
Ein Kommentar steht in Modula in Klammern mit Sternchen (*
Anfang und Ende *) und kann auch über mehrere Zeilen
gehen. Ansonsten gilt wie in Pascal (und C++) auch : Anweisungen schließt
man mit einem Semikolon ; ab (zumindest meistens).
Prinzip von Modula 3 |
Das Hauptprogramm |
| "Main.m3" |
||
MODULE Main; |
Das Makefile |
| "m3makefile" |
||
| import("libm3") implementation("Main") program("Main") |
Das Hauptprogramm |
| "Name.m3" |
||
MODULE Name EXPORTS Main; |
Das Makefile |
| m3makefile |
||
| import("libm3") implementation("Name") program("Main") |
Variablen |
VAR variablenname : typ;Der Name kann nahezu beliebig gewählt werden. Verboten sind Sonderzeichen (ausser "_") und Leerzeichen, sowie Zahlen am Anfang des Namens.
VARWenn man zwei Variablen vergleichen will, dann muss man beachten, dass beide Variablen vom gleichen Typ sind.
name : typ; (* OK *) geht_auch : typ; (* OK *) name2 : typ; (* OK *)
12hallo : typ; (* Falsch! - Zahl am Anfang *) _name : typ; (* Falsch! - Sonderzeichen am Anfang *) hallo du : typ; (* Falsch! - Leerzeichen nicht erlaubt *) name! : typ; (* Falsch! - unerlaubtes Sonderzeichen *)
(* Zuweisung von Werten *)
name := wert;
| Typ |
Beschreibung |
||
CARDINAL |
natürliche
Zahlen |
||
INTEGER |
ganze Zahlen |
||
REAL |
Gleitkommazahlen (Zahl
mit Komma) |
||
LONGREAL |
Gleitkommazahlen (Zahl
mit Komma) |
||
EXTENDED |
Gleitkommazahlen (Zahl
mit Komma) |
||
CHAR |
ein Zeichen |
||
TEXT |
Zeichenkette |
||
BOOLEAN |
Wahrheitswert(TRUE,
FALSE) |
Konstanten |
CONSTDer Name der Konstanten ist beliebig, solange er den Regeln für Variablennamen entspricht.
const_int = 2;
const_real = 2.0;
const_text = "Hallo";
CONSTBeispiele : const.m3
const_int : INTEGER = 2;
const_real : REAL = 2.0;
const_ext : LONGREAL = 2.0; (* ist etwas anderes als const_real! *)
const_text : TEXT = "Hallo";
Variablentypen und Umwandlungen |
CARDINAL, INTEGER |
| Typ |
FIRST() |
LAST() |
||
CARDINAL |
0 |
2.147.483.647 |
||
INTEGER |
-2.147.483.648 |
2.147.483.647 |
VAR
Variable : CARDINAL; (* Deklaration *)
Variable2 : CARDINAL := 5; (* Deklaration mit default wert *)
VAR Variable : INTEGER; (* Deklaration *) Variable2 : INTEGER := 5; (* Deklaration mit default Wert *)
(* Ein- und Ausgabe *)beachten sie dabei, das Fehler nicht abgefangen werden! Wenn man eine negative Zahl, einen Buchstaben oder eine Zahl ausserhalb des Wertebereiches eingibt, bricht das Programm mit einem Laufzeitfehler ab. (siehe Exceptions) Sie werden sie durch einen Hinweis vom Compiler auf dieses Problem hingewiesen.
variable := IO.GetInt(); (* einlesen; Modul IO *)
IO.PutInt(variable; (* ausgaben; Modul IO *)
(* CARDINAL in INTEGER *)
integer := cardinal; (* Modul IO *)
(* CARDINAL in TEXT *) text := Fmt.Int(cardinal); (* Modul Fmt *)
(* INTEGER in CARDINAL *) integer := ABS(cardinal); (* Modul IO *)
(* CARDINAL in TEXT *) text := Fmt.Int(integer); (* Modul Fmt *)
REAL, LONGREAL, EXTENDED |
| Typ |
FIRST() |
LAST() |
||
REAL |
1.17549435E-38 |
3.40282347E+38 |
||
LONGREAL |
2.2250738585072014D-308 |
1.7976931148623157D+308 |
||
EXTENDED |
2.2250738585072014X-308 | 1.7976931148623157X+308 |
VAR
real : REAL [ := 5.0 ]; (* Deklaration mit default Wert - ACHTUNG! *)
longreal : LONGREAL [ := 5.0 ]; (* bei Gleitkommavariablen muss die Nach- *)
extended : EXTENDED [ := 5.0 ]; (* kommastelle immer mit angegeben werden! *)
(* Eingabe *)folgende Umwandlungen sind interessant :
real := IO.GetReal(); (* Modul IO *) longreal := ?; (* mir unbekannt *) extended := ?. (* mir unbekannt *)
(* Ausgabe *)
IO.PutReal(real); (* Modul IO *)
(* INTEGER in REAL, LONGREAL, EXTENDED *)
real := FLOAT(integer); (* Modul IO *)
longreal := FLOAT(integer, LONGREAL); (* Modul IO *)
extended := FLOAT(integer, EXTENDED); (* Modul IO *)
(* REAL in INTEGER, LONGREAL, EXTENDED *) integer := TRUNC(real); (* schneidet die Nachkommastellen ab *) integer := ROUND(real); (* rundet auf die entsprechende ganze Zahl *) integer := FLOOR(real); (* ergibt die kleinere ganze Zahl *) integer := CEILING(real); (* ergibt die grössere ganze Zahl *)
longreal := FLOAT(integer, LONGREAL); extended := FLOAT(integer, EXTENDED);
(* für LONGREAL und EXTENDED sind die Umwandlungen entsprechend *)
CHAR, TEXT |
VARinteressante Umwandlungen sind :
zeichen := CHAR;
string := TEXT;
(* einlesen *) zeichen := IO.GetChar(); (* Modul IO *) text := IO.GetLine();
(* ausgeben *) IO.PutChar(zeichen); IO.Put(text);
(* CHAR in TEXT, INTEGER *)Beispiele : zahlen.m3, text.m3
text := Text.FromChar(zeichen); (* Modul Text *)
integer := ORD(zeichen); (* wandelt zeichen in den zugehöringen ASCII-Code *)
zeichen := VAL(integer); (* Umkehrfunktion dazu z.b. VAL(66) = 'B' *)
(* TEXT in CHAR *) zeichen := Text.GetChar(text, pos); (* gibt das Zeichen an der Stelle pos aus text zurück *)
BOOLEAN |
VARFür weitere Funktionen lesen sie bitte die Befehlsreferenz. Dort sind einige nützliche Funktionen beschrieben, die man des öfteren zur Umwandlung oder Bearbeitung von Variablen gebrauchen kann.
boolean : BOOLEAN; (* TRUE oder FALSE *)
(* einlesen - selber schreiben =/ *) get_boolean(); (* selbst verfasst *)
(* ausgben durch Umwandlung in TEXT *) IO.Put(Fmt.Bool(boolean)); (* gibt "TRUE" oder "FALSE" aus, Modul Fmt *)
(* BOOLEAN in TEXT *) text := Fmt.Bool(boolean); (* Modul Fmt - siehe oben *)
Typen |
VAR matrix3d : ARRAY OF ARRAY OF ARRAY OF INTEGER;kann man auch übersichtlicher und damit besser lesbar schreiben :
TYPEWenn sie jetzt sagen : "ist ja schön und gut, aber ist das alles?", bleibt nur ja als Antwort. Aber sie werden diese Möglichkeit bald zu schätzen wissen, vor allem wenn man solche Konstrukte als Parameter übergibt, wird man es bald leid, ständig die vollständige Bezeichnung aufzuschreiben und es gibt durchaus auch geringfügig kompliziertere Konstrukte als das Beispiel =).
int_array : ARRAY OF INTEGER;
int_matrix : ARRAY OF int_array;
int_matrix3d : ARRAY OF int_matrix;
VAR matrix3d : int_matrix;
Beispiele : simple_type.m3, array_type.m3
Records |
TYPEDabei können die Variablen in einem Record von einem beliebigen Typ, also auch wiederum RECORDS sein.
name = RECORD
[ Variable1 = INTEGER; ]
[ ... ]
END;
TYPE
person = RECORD
name : TEXT;
alter : INTEGER;
END;
VARWichtig ist nur das man die einzelnen Elemente mit "Variablenname.Element" ansprechen kann. Benutzt man mehrere Records ineinanderverschachtelt, so funktioniert das genauso :
ich = person;
du = person;
BEGIN ich.name := "daniel"; (* zugriff erfolgt mittels *) ich.alter := 235; (* "." und bezeichener *)
du := ich; (* alle elemente von ich werden in du kopiert *) du.name := ich.name; (* hi bruder =) - kopiere nur ein element *) ... END.
TYPE
person = RECORD
name : TEXT;
alter : INTEGER;
END;
student = RECORD typ : person; (* verwende personen-typ *) fach : TEXT; (* und noch was neues =) *) END;
VAR ich : student; (* jetzt bin ich student *) BEGIN ich.typ.name := "daniel"; (* hier greift man über "typ" *) (* auf name zu (personen - record) *) ich.typ.alter := 235; (* hier genauso *) ich.fach := "überlebenskunst"; (* fach im typ (record) student *) ... END.
Zeiger - Dynamische Speicherverwaltung |
TYPE zeiger_typ = REF Datentyp; (* Zeiger auf Datentyp *)Eine Zeigervarible legt man nun wie gewohnt an, achten sie darauf, sie möglichst gleich zu initialisieren!
VAR zeiger : zeigertyp := NIL;Hat ein Zeiger den Wert NIL (not in list), so heißt das, das er auf keine Adresse im Speicher zeigt, er hat sozusagen keinen Wert. Benutzt man zeiger, ohne ihm vorher eine gültige Adresse zuzuweisen, so zeigt er auf eine zufällige Adresse im Speicher. Weist man ihm dann einen Wert zu, so überschreibt er diese, was bei manchen Betriebssystemen schlimmstenfalls zu Abstürzen führen kann, bzw. können sie so ungewollt andere Variablen ihres Programmes überschreiben oder verfälschen. Hier ist also Vorsicht geboten (!) haben sie aber keine Scheu, Zeiger zu benutzten - sie machen vieles einfacher.
TYPE int_ptr : REF INTEGER; (* zeiger auf integer *)Sie sehen also, man kann Zeiger in vielfältiger Weise einsetzten. Nun nochmal kurz die Syntax von NEW :
VAR int : INTEGER; (* variable vom typ integer *) VAR ptr1, ptr1 : int_ptr; (* 2 zeiger auf integer-variablen *)
(* vermeiden sie soetwas : *) ptr1^ := 2131; (* Achtung! Adresse des Zeigers noch unklar *)
(* das ist ok *) ptr1 := NEW(INTEGER); (* ptr bekommt eine Adresse auf einen freien *) (* Speicherplatz für eine Integer-Zahl zugewiesen *) ptr1^ := 2131; (* ok, ptr zeigt auf gültigen Speicherplatz *) ptr2 := ptr1; (* Zeiger 2 zeigt nun auf den gleichen Speicherplatz *)
ptr2^ := 23; IO.PutInt(ptr1^); (* gibt "23" aus, da beide Zeiger auf den gleichen *) (* Speicherplatz zeigen *) ptr1 := NIL; (* Zeiger 1 zeigt nirgendwohin *) ptr2 := NIL; (* Zeiger 2 zeigt nirgendwohin *) (* auf den Speicherplatz mit der 23 kann nun nicht mehr zugegriffen werden! *)
zeiger := NEW(REF Datentyp [, [Dim1] [, Dim2] ]);NEW gibt immer einen Zeiger auf Datentyp zurück. REF kann man weglassen, wenn Datentyp bereits ein "Zeiger-auf" ist :
TYPE zeiger_auf_int : REF INTEGER;Die Parameter Dim1, Dim2, ... kommen zum Einsatz, wenn man Zeiger auf Arrays verwenden will. Dim1 bezeichnet dann die Grösse der 1. Dimension, Dim2 die der Zweiten und so weiter :
VAR zeiger := NEW(zeiger_auf_int);
TYPE zeiger_auf_array_array_int : REF ARRAY OF ARRAY OF INTEGER;Wie man nun einem Zeiger die Adresse einer Variablen zuweisst, habe ich noch nicht herausgefunden ( &Variable, wie in C++ funktioniert nicht), aber man kann sich funktionen schreiben, die soetwas leisten (siehe Beispiele).
VAR zeiger := NEW(zeiger_auf_array_array_int, 10,15);
(* Zeiger auf ARRAY[0..9] OF ARRAY[0..14] OF INTEGER *)
TYPEWenn Modula auch in vielen Bereichen meiner Meinung nach umständlich und unzureichend ist, so ist es doch auch möglich, Zeiger auf Zeiger sowie Zeiger auf Funktionen zu programmieren. Damit kann man dann auch sehr schön dynamisch den Programmlauf ändern, indem man mit Feldern von Zeigern oder Feldern von Funktionen arbeitet. Das zu erklären, würde hier allerdings zu lang dauern und zugegeben, es fällt mir im Moment auch kein gutes Beispiel dazu ein ;). Aber die Variante möchte ich trotzdem hier kurz mit angeben.
person_ptr = REF person; (* zeiger auf personen-record *)
person = RECORD (* das record selber *)
name : TEXT;
alter : INTEGER;
END;
TYPE array_int : ARRAY OF ARRAY OF INTEGER; array_ptr : REF array_int;
VAR ich : person_ptr; (* ich ist jetzt ein zeiger *) du : person_ptr; (* du auch =) *)
VAR fld : array_ptr; (* zeiger auf 2d-integer array *)
BEGIN ich := NEW(person_ptr); (* reserviere speicher für eine variable vom *) (* typ person und gibt die addresse zurück *) ich^.name := "daniel"; (* ^ (Carret) bedeudet, ich greife auf die Variable zurück *) (* . weil die Variable ein record (sprich zusammengesetzt) ist *) du := ich; (* du zeigt nun auf die gleiche speicherstelle *) du^.name := "neu"; (* ist das gleicher wie : *) ich^.name := "alt"; (* das hier! *)
du^.name := "daniel"; (* da "du" und "ich" auf die gleiche "stelle" zeigen *) IO.Put(ich^.name & "\n"); (* wird hier "daniel" ausgegeben *)
du := NEW(person_ptr); (* du zeigt auf ein neues element *) du^ := ich^; (* ^ greift auf elemente zu -> kopiert "ich" in "du" *)
fld := NEW(array_ptr, 10,10); (* zeiger auf auf 10x10 feld mit integer einträgen*) fld^[0,0] := 12; (* erstes element ist jetzt 0 *) (*
folgendes beispiel setzt alle felder auf "23" - für ein beliebig grosses feld
dabei ist zu beachten, wie "spalte" und "zeile" bestimmt wird! nicht vertauschen,
da sonst chaos raus kommen kann bzw. man über die grenzen schreiben könnte
*) FOR spalte := FIRST(fld^) TO LAST(fld^) DO FOR zeile := FIRST(fld^[FIRST(fld^)]) TO LAST(fld^[FIRST(fld^)]) DO fld^[spalte, zeile] := 23; END; END;
END.
TYPEEine kleine Spielerei damit kann man sich in double_ptr.m3 anschauen (siehe Beispiele) - lasst eurer kreativität freien Lauf ! =)
funktion = PROCEDURE(); (* hier sind auch parameter und rückgabewerte möglich *)
funk_ptr = REF funktion; (* das ist ein zeiger auf funktionen des typs funktion *)
ptr = REF INTEGER; (* zeiger auf ein integer *)
ptr_ = REF ptr; (* zeiger auf einen "zeiger auf integer" *)
Schleifen und Wiederhohlungen |
FOR |
(* Syntax *Beachten sie bitte, das Variable vorher nicht deklariert werden muss. Anfangswert und Endwert können selbstverständlich auch Variablen sein (ebenso Schrittweite).
FOR Variable := Anfangswert TO Endwert [BY Schrittweite] DO
[ Anweisung; ]
[ ... ]
END;
WHILE |
(* Syntax *)Hier ist es nun notwendig, Variable vorher zu deklarieren. Sie muss BOOLEAN sein, wenn man keine weitere Bedingung angibt, ansonsten ist fast jeder beliebige Typ erlaubt, solange auf einen Wahrheitswert geprüft wird. Möglich wären auch mehrere Bedingungen wie zum Beispiel :
WHILE (Variable [= Bedingung]) DO
[ Anweisung ]
[ ... ]
END;
WHILE ((Variable1 = Bedingung1) OR (Variable2 # Bedingung2)) DO
Anweisung1;
Anweisung2;
...
END;
REPEAT |
(* Syntax *)Auch hier kann man die Bedingung nur weglassen, wenn man Variable vom Typ BOOLEANdeklariert hat. Auch hier kann man mehrere Abbruchbedingungen angeben :
REPEAT
[ Anweisung ]
[ ... ]
UNTIL (Variable [= Bedingung]);
REPEATAchten sie darauf sinnvoll zu Klammern! Im obrigen Beispiel könnte man die äusseren Klammern beispielsweise auch weglassen, zur Übersichtlichkeit und besseren Lesbarkeit, setzte ich sie jedoch meistens.
Anweisung1;
Anweisung2;
...
UNTIL ((Variable1 = Bedinung1) OR (Variable2 = Bedingung2));
Vergleiche und Bedingungen |
CASE |
(* Syntax *)Wichtig ist hier zum einen, das Ausdruck und Ausdruck2 konstant sein müssen, das heißt sie dürfen hier keine Variablen erwenden. Desweiteren kann man den ELSE-Zweig weglassen :
CASE Variable OF
|Ausdruck => Anweisung1;
|Ausdruck2 => Anweisung2;
Anweisung3;
[ ... ]
[|Ausdruck3 => Anweisung10;]
ELSE
[ Anweisung_Else1; ]
[ Anweisung_Else2; ]
[ ... ]
END;
CASE Variable OFBekommt dafür aber eine Wahrnung vom Compiler, das nicht alle möglichen Zustände von Variable betrachtet wurden.
[ ... ]
END;
CASE Variable OF
ELSE
END;
IF-THEN-ELSE |
(* syntax *)Wenn man auch den Fall betrachten will, wen eine Bedingung nicht erfüllt ist, dann benutzt man üblicherweise die Wenn-Dann-Sonst-Bedingung. Umgangssprachlich formuliert : "Wenn die Bedingung erfüllt ist. dann bearbeite folgende Anweisungen sonst mache folgendes ..."
IF Bedingung THEN
[ Anweisung; ]
[ ... ]
END;
IF Bedingung THENWie auch sonst kann man die Bedingung komplexer und ausführlicher Schreiben und mehrere Variablen auf ihren Zustand prüfen. Achten sie auf sinnvolle Bedinungen!Folgende Variante ist auch erlaubt, aber wenig sinnvoll :
[ Anweisung; ]
[ ... ] (* kein END vor ELSE ! *)
ELSE
[ Anweisung; ]
[ ... ]
END; (* abschliessendes END *)
IF 2 = 2 THEN [ Anweisung; ] END;Beispiele : ifthen.m3, case.m3
Prozeduren |
PROCEDURE name([parameter : Typ]) [ : Rückgabewert] =Allgemein besitzen Prozeduren auf jeden Fall einen Namen name und einen Anweisungsblock (das zwischen BEGIN
[ Variablendeklaration ] BEGIN [ Anweisung; ] [ Anweisung; ] [ ... ] END name;
PROCEDURE name() =Eine Prozedur ohne Anweisung ist denkbar, hat aber wenig Sinn. Um Prozeduren sinnvoller nutzen zu können und sie vor allem allgemeingültig zu schreiben, gibt es die Möglichkeit Parameter zu übergeben und zwar auf drei Art und Weisen. Erstens, man übergibt eine Kopie der Variable, zweitens man übergibt die Variable als Referenz (dann wird direkt mit der Variable gearbeitet, ohne eine Kopie davon im Speicher anzulegen) oder man übergibt sie als konstanten Parameter, damit darf man sie innerhalb der Prozedur nicht mehr verändern. Sinnvoll wird die Übergabe per Wert vorallem dann, wenn man besonders grosse Objekte als Parameter übergeben muss, da sonst für die Kopie zuerst neuer Speicher reserviert und anschliessend das Objekt noch kopiert werden muss, was viel Zeit beansprucht. Eine Alternative währe da die Übergabe eines Zeigers auf das entsprechende Objekt.
BEGIN
[ Anweisung; ]
[ Anweisung; ]
[ ... ]
END name;
| Schlüsselwort |
Bedeutung |
Erklärung |
||
| VAR | Übergabe per Referenz |
es wird direkt mit
der Variablen gearbeitet und nur ein Alias eingeführt |
||
| VALUE | Übergabe per Wert |
man arbeitet mit einer
Kopie der Variablen, Änderungen haben keinen Einfluss auf die Variable
selbst |
||
| READONLY |
Übergabe per Referenz,
aber nur lesbar |
man arbeitet direkt
mit der Variablen, darf diese aber nicht verändern |
PROCEDURE name([parameter : Typ]) : Rückgabewert =Ob mit oder ohne Parameter bleibt ihnen überlassen =).
[ Variablendeklaration ]
BEGIN
[ Anweisung; ]
[ Anweisung; ]
[ ... ]
END name;
Was sind rekursive Prozeduren ? |
PROCEDURE fakultaet(n : INTEGER) : INTEGER =
BEGIN
IF (n <= 1) THEN RETURN(n); (* 1! = 1 und 0! = 0, andere Fälle *)
(* (für negative Zahlen) werden nicht behandelt! *)
ELSE RETURN(n*fakultaet(n-1)); (* 2! = 2x1!; 3! = 3x2! da 2! = 2*1! und *)
(* 1! = 1 => 3! = 3*2*1 = 6 *)
END fakultaet;
Module (Bibliotheken, Librarys, Units) |
| Dateiname |
Struktur |
Beschreibung |
|||
|
INTERFACE Lib; |
Interface - hier definiert man alle benötigten
Schnittstellen, wie Prozeduren, diverse Typen, kurz alles, was man von aussen
verwenden können soll und muss |
|||
|
MODULE Lib EXPORTS Lib; |
Implementation - hier werden die Prozeduren
bzw. die Anweisungen codiert bzw. programmiert. |
|||
|
MODULE Main; |
Hauptprogramm - hier wird das Modul eingebunden,
ansonsten alles wie gewohnt (Achtung! : kein EXPORTS nach dem Main ) |
|||
|
import("libm3")
|
Makefile - auch hier müssen wir das
Modul angeben, da die Implementation auch übersetzt werden muss ACHTUNG! "libm3" hat nichts mit unserem Modul Lib zu tun, sondern muss immer mit eingebunden werden. |
| Alle hier aufgeführten Definitionen
von Prozeduren und Typen ect. sind öffentlich und können vom Benutzer
der Bibliothek verwendet werden |
| Dieser Teil ist für den Benutzer der
Bibliothek eher unerheblich, da ihn die genaue implementation der Lösung
nicht interessiert. Er muss nur wissen, welche Funktionen ihm zur Verfügung
stehen. Achtung! : alle Funktionen, die nicht im Interface benannt werden,
aber man zusätzlich implementiert und benutzt sind nicht öffentlich,
d.h. die Prozeduren im Modul können diese zur Problemlösung verwenden,
der Benutzer des Moduls jedoch nicht. |
INTERFACE Lib; |
MODULE Lib EXPORTS Lib; |
MODULE Main; |
| TimeStamp |
speichern sie einfach alle Dateinen nocheinmal
nacheinander ab |
||
| Multiple Link |
hier löschen sie am besten alle compilierten
Dateien (unter Linux das LINUXLIBC6-Verzeichnis) |
Objekte |
TYPEIch habe mich nicht verschrieben - die Methoden des Objekts sind die gross geschriebenen Prozeduren, das klein Geschriebene ist die Implementation der entsprechenden Methode. Sie erhält immer als ersten Parameter vom Typ des Objekts, der beim Aufruf aber nicht mit angegeben werden muss. Esexistiert leider kein standartmäßiger *this - Zeiger wie in C/C++ (Java glaub ich auch). Wer das braucht, arbeitet am Besten mit Zeigern und übergibt die Adresse des Objekts bei der Initialisierung.
simple = OBJECT
int : INTEGER; (* die eigenschaften des objekts *)
METHODS
Init() := init; (* die methoden *)
Print() := print;
Add(i : INTEGER) := add;
END;
PROCEDURE init(obj : simple) =
BEGIN
obj.int := 0;
END init;
PROCEDURE print(obj : simple) =
BEGIN
IO.Put(Fmt.Int(obj.int) & "\n");
END print;
PROCEDURE add(obj : simple; i : INTEGER) =
BEGIN
obj.int := obj.int + i;
END add;
VAR objekt := simple;
BEGIN
objekt := NEW(simple); (* neues objekt anlegen *)
objekt.Init(); (* int mit 0 initialisieren *)
objekt.Add(5);
objekt.Print();
END Main.
Threads (ein kleines Multithreading Progamm in Modula 3) |
MODULE Main;Ein weiters Beispiel wäre (mit 2 Threads konkurierend, wobei beide die selbe Funktion benutzen)
IMPORT Thread, IO;
TYPE Closure = Thread.Closure OBJECT OVERRIDES apply := Printld; END;
VAR cl := NEW(Closure); (* neues thread objekt *) thread : Thread.T; (* variable fuer rueckgabewert *)
PROCEDURE Printld(c : Closure) : REFANY = (* c : Closure muss sein, da apply so definiert ist *) BEGIN FOR i := 1 TO 100000000 DO IF (i MOD 100000) = 0 THEN IO.Put("T"); END; END; RETURN(NIL); END Printld;
BEGIN thread := Thread.Fork(cl); (* thread starten, rückgabewerte speichern *) (* main prozess *) FOR d := 1 TO 100050000 DO IF (d MOD 100000) = 0 THEN IO.Put("M"); (* M für main ausgeben *) END; END; EVAL Thread.Join(thread); (* auf thread warten *) IO.Put("\n"); END Main.
MODULE Main;
IMPORT Thread, IO;
TYPE Closure = Thread.Closure OBJECT number : INTEGER; (* fuer die identifizierung *) OVERRIDES (* default ueberschreiben *) apply := Printld; (* neue aufgabenfunktion *) END;
VAR th_cl_1 := NEW(Closure, number := 1); (* objekt fuer Thread 1 *) th_cl_2 := NEW(Closure, number := 2); (* objekt fuer Thread 2 *) thread1, thread2 : Thread.T; (* fuer rueckgabewerte *)
PROCEDURE Printld(c : Closure) : REFANY = BEGIN FOR i := 1 TO 100000000 DO IF (i MOD 100000) = 0 THEN IF c.number = 1 THEN IO.Put("1"); (* bearbeitet von Thread 1 *) ELSIF c.number = 2 THEN IO.Put("2"); (* bearbeitet von Thread 2 *) END; END; END; RETURN(NIL); (* just nothin =) *) END Printld;
BEGIN thread1 := Thread.Fork(th_cl_1); (* Thread 1 starten *) thread2 := Thread.Fork(th_cl_2); (* Thread 2 starten *)
(* irgendwas machen *) FOR d := 1 TO 100050000 DO IF (d MOD 100000) = 0 THEN IO.Put("M"); END; END; (* fertig mit irgendwas =) *)
EVAL Thread.Join(thread1); (* auf Thread 1 warten *) EVAL Thread.Join(thread2); (* auf Thread 2 warten *)
IO.Put("\n"); END Main.
Exceptions (Ausnahmen abfangen und werfen) |
TRYim Allgemeinen sieht das etwas so aus :
int := IO.GetInt(); (* kritische operation *)
EXCEPT
IO.Error => IO.Put("Fehler!\n"); (* Fehlerbehandlung für IO.Error *)
END;
TRYWill man selber Exceptions werfen, macht man folgendes :
Anweisung;
[ Anweisung2; ]
EXCEPT
|Fehler1 => Anweisung;
[ ... ]
[|Fehler2 => Anweisung ]
END;
EXCEPTION fehler; (* deklaration einer exception *)Abschließend kann man sagen, das Exceptions dazu verwendet werden können um den Benutzer zu sagen, das Fehler aufgetreten sind und ihm überlässt, was er jetzt machen will.
EXCEPTION input_error; (* das gleiche *)
PROCEDURE test() : INTEGER RAISES {fehler} = (* prozedur wirft eventuell fehler *) BEGIN [ ... ] (* hier steht irgendwas *) IF Bedingung THEN RAIS fehler; END; (* hier wird die exception aktiv *) [ ... ] RETURN(0); END test;
PROCEDURE print() RAISES {fehler, input_error} = (* mehr sind auch erlaubt *) BEGIN [... ] (* wirft irgendwo fehler und input_error *) END print;
BEGIN TRY print(); (* verwende kritische anweisung *) EXCEPT |fehler => Anweisung; (* fehler ist aufgetreten => tu was *) |input_error => Anweisung2; (* input_error ist aufgetreten *) END; END Main.
Befehlsreferenz |
| Interface IO |
Rd, Wr |
| PROCEDURE Put (txt
: TEXT; wr : Wr.T := NIL); (* gibt txt auf dem Bildschirm aus - wr : ? |
*) |
||
| PROCEDURE PutChar (ch
: CHAR; wr : Wr.T := NIL); (* gibt ch auf dem Bildschirm aus - wr : ? |
*) |
||
| PROCEDURE PutInt (n
: INTEGER; wr : Wr.T := NIL); (* gibt n mittels Fmt.Int(n) auf dem Bildschirm aus - wr : ? |
*) |
||
| PROCEDURE PutReal (r
: REAL; wr : Wr.T := NIL); (* gibt r mittels Fmt.Real(r)auf dem Bildschirm aus - wr : ? |
*) |
||
| PROCEDURE GetLine (rd
: Rd.T := NIL) : TEXT; (* ließt eine Zeile von der tastatur und gibt diese als TEXT zurück |
RAISES {Error} | *) |
|
| PROCEDURE GetInt (rd
: Rd.T := NIL) : INTEGER; (* ließt eine Dezimalzahl von der Tastatur und gibt diese als INTEGER zurück |
RAISES {Error} | *) |
|
| PROCEDURE GetReal (rd
: Rd.T := NIL) : REAL; (* ließt eine nummer von der Tastatur und gibt diese als REAL zurück |
RAISES {Error} | *) |
| Interface Word |
|
| PROCEDURE Divide (x, y
: INTEGER) : INTEGER; (* entspricht x DIV y |
*) |
|
| PROCEDURE Mod (x, y
: INTEGER) : INTEGER; (* entspicht x MOD y |
*) |
|
| PROCEDURE LT (x, y
: INTEGER) : BOOLEAN; (* "lower then" (kleiner als) entspricht x < y |
*) |
|
| PROCEDURE LE (x, y
: INTEGER) : BOOLEAN; (* "lower equal" (kleiner gleich) entspricht x <= y |
*) |
|
| PROCEDURE GT (x, y
: INTEGER) : BOOLEAN; (* "greater then" (grösser als) entspricht x > y |
*) |
|
| PROCEDURE GE (x, y
: INTEGER) : BOOLEAN; (* "greater equal" (grösser gleich) entspricht x >= y |
*) |
|
| PROCEDURE And (x, y
: INTEGER) : INTEGER; (* bitweises UND von x und y |
*) |
|
| PROCEDURE Or (x, y
: INTEGER) : INTEGER; (* bitweises ODER von x und y |
*) |
|
| PROCEDURE Xor (x, y
: INTEGER) : INTEGER; (* bitweises XOR von x und y |
*) |
|
| PROCEDURE Not (x, y
: INTEGER) : INTEGER; (* bitweise Negation von x und y |
*) |
| Interface Text |
Word |
| PROCEDURE Equal (word1, word2
: TEXT) : BOOLEAN; (* gibt TRUE zurück, wenn word1 und word2 Übereinstimmen (gleiche Länge beachten !) |
*) |
|
| PROCEDURE Length (text :
TEXT) : CARDINAL; (* gibt die Länge von text zurück (fängt bei 0 an zu Zählen) |
*) |
|
| PROCEDURE Cat (word1, word2
: TEXT) : TEXT; (* gibt die Verknüpfung von word1 und word2 zurück |
*) |
|
| PROCEDURE Sub (text : TEXT;
pos, anzahl : CARDINAL) : TEXT; (* gibt anzahl Zeichen aus text Angefangen bei pos zurück. |
*) |
|
| PROCEDURE Empty (text : TEXT)
: BOOLEAN; (* gibt TRUE zurück, wenn textleer ("") ist, äquivalent zu Text.Equal(text, "") |
*) |
|
| PROCEDURE FromChar (char :
CHAR) : TEXT (* wandelt ein Zeichen char in den Typ TEXT um |
*) |
|
| PROCEDURE GetChar (text :
TEXT; pos : CARDINAL) : CHAR; (* gibt das Zeichen an der Stelle pos in text zurück (Laufzeitfehler, wenn pos > Text.Length(pos) ist) |
*) |
|
| PROCEDURE FindChar (text :
TEXT; zeichen : CHAR; start : INTEGER
:= 0) : INTEGER; (* gibt die kleinste Position zwischen start und Length(text) zurück, an der zeichen in text auftritt, -1 wenn es nicht vorkommt |
*) |
|
| PROCEDURE FindCharR (text :
TEXT; zeichen : CHAR; end : INTEGER
:= LAST(INTEGER)) : INTEGER; (* gibt die grösste Position zwischen 0 und end zurück, an der zeichen in text auftritt, -1 wenn es nicht vorkommt |
*) |
|
| PROCEDURE SetChars (VAR array
: ARRAY OF CHAR; text : TEXT); (* schreibt den Text aus text in ein Char-Array (als Folge von Chars) - bedenken sie, dass das array gross genug sein muss |
*) |
|
| PROCEDURE FromChars (READONLY
array : ARRAY OF CHAR) : TEXT; (* wandelt das Char-Array in einen Text um und gibt diesen zurück, das Array wird nicht verändert |
*) |
| Interface Fmt |
|
| PROCEDURE Bool (bool : BOOLEAN) :
TEXT; (* gib "TRUE" oder "FALSE" vom Typ TEXT zurück |
*) |
|
| PROCEDURE Int (int : INTEGER;
base : Base := 10) : TEXT; (* wandelt die Zahl int interpretiert zu base im Format TEXT zurück (default ist Basis 10, Base = {2..16} |
*) |
|
| PROCEDURE Char (char : CHAR) :
TEXT; (* gibt das Zeichen char im Format TEXT zurück |
*) |
|
| PROCEDURE Addr (n : ADRESS;
base : Base := 16) : TEXT; (* gibt "NIL" oder den Wert von n zur Basis base im Format TEXT zurück, Base = {2..16} |
*) |
|
| PROCEDURE Ref (n : REFANY;
base : Base := 16) : TEXT; (* gibt oder den Wert von n zur Basis base im Format TEXT zurück, Base = {2..16} |
*) |
|
| PROCEDURE Real (real : REAL;
prec : CARDINAL := 6; style : Style := Style.Mix) :
TEXT; (* gibt real mit prec Nachkommastellen formatiert mit style vom Typ TEXT zurück, Style = {Flo, AltFlo, Sci, AltSci, Mix} |
*) |
|
| PROCEDURE LongReal (longreal :
LONGREAL; prec : CARDINAL := 6; style :
Style := Style.Mix) : TEXT; (* gibt longreal mit prec Nachkommastellen formatiert mit style vom Typ TEXT zurück, Style = {Flo, AltFlo, Sci, AltSci, Mix} |
*) |
|
| PROCEDURE Extended (ext :
EXTENDED; prec : CARDINAL := 6; style : Style :=
Style.Mix) : TEXT; (* gibt ext mit prec Nachkommastellen formatiert mit style vom Typ TEXT zurück, Style = {Flo, AltFlo, Sci, AltSci, Mix} |
*) |
|
| PROCEDURE Pad (text : TEXT;
length : CARDINAL; char : CHAR := '';
align : Align := Align.Right) : TEXT; (* gibt text bis zu length aufgefüllt mit char (entprechend align, dh. rechts oder linksseitig) im Format TEXT zurück |
*) |
| Interface Scan |
Word, Lex, FloatMode |
| PROCEDURE Bool (text : TEXT) :
BOOLEAN; (* versucht text in BOOLEAN umzuwandeln |
RAISES {Lex.Error} |
*) |
|
| PROCEDURE Int (text : TEXT) :
INTEGER; (* versucht text in ein INTEGER umzuwandeln |
RAISES {Lex.Error, FloatMode.Trap} |
*) |
|
| PROCEDURE Unsigned (text :
TEXT) : Word.T; (* versucht text in den Typ Word.T umzuwandeln |
RAISES {Lex.Error, FloatMode.Trap} |
*) |
|
| PROCEDURE Real (text : TEXT) :
REAL; (* versuche text in eine Real-Zahl umzuwandeln |
RAISES {Lex.Error, FloatMode.Trap} |
*) |
|
| PROCEDURE LongReal (text :
TEXT) : LONGREAL; (* versucht text in LONGREAL unzuwandeln |
RAISES {Lex.Error, FloatMode.Trap} |
*) |
|
| PROCEDURE Extended (text :
TEXT) : EXTENDED; (* versucht text in EXTENDED umzuwandeln |
RAISES {Lex.Error, FloatMode.Trap} |
*) |
|
| PROCEDURE Char (text : TEXT) :
CHAR; (* versuche text in ein CHAR umzuwandeln |
RAISES {Lex.Error} |
*) |
| Interface Math |
CONST
Pi = 3.1415926535897932384626433833;
LogPi = 1.1447298858494001741434273514;
SqrtPi = 1.7724538509055160272981674833;
E = 2.7182818284590452353602874714;
Degree = 0.017453292519943295769236907684; (* One degree in radians *)
| PROCEDURE exp (x : LONGREAL) :
LONGREAL; (* gibt E hoch x zurück |
*) |
||
| PROCEDURE log (x : LONGREAL) :
LONGREAL; (* gibt den natürlichen Logarithmus von x zurück (zur Basis E) |
*) |
||
| PROCEDURE log10 (x : LONGREAL) :
LONGREAL; (* gibt den Logarithmus zur Basis 10 zurück |
*) |
||
| PROCEDURE log1p (x : LONGREAL) :
LONGREAL; (* gibt log(x + 1) zurück, auch für kleine x |
*) |
||
| PROCEDURE pow (x, y :
LONGREAL) : LONGREAL; (* gibt x hoch y zurück |
*) |
||
| PROCEDURE sqrt (x : LONGREAL) :
LONGREAL; (* gibt die Quadratwurzel von x zurück |
*) |
||
| PROCEDURE cos (x : LONGREAL) :
LONGREAL; (* gibt den Cosinus von x zurück (x im Bogenmass) |
*) |
||
| PROCEDURE sin (x : LONGREAL) :
LONGREAL; (* gibt den Sinus von x zurück (x im Bogenmass) |
*) |
||
| PROCEDURE tan (x : LONGREAL) :
LONGREAL; (* gibt den Tangens von x zurück (x im Bogenmass) |
*) |
||
| PROCEDURE acos (x : LONGREAL) :
LONGREAL; (* gibt den arc Cosinus von x zurück (x im Bogenmass) |
*) |
||
| PROCEDURE asin (x : LONGREAL) :
LONGREAL; (* gibt den arc Sinus von x zurück (x im Bogenmass) |
*) |
||
| PROCEDURE atan (x : LONGREAL) :
LONGREAL; (* gibt den arc Tangens von x zurück (x im Bogenmass) |
*) |
||
| PROCEDURE atan2 (x, y :
LONGREAL) : LONGREAL; (* gibt den arc Tangens x/y zurück (x,y im Bogenmass) |
*) |
||
| PROCEDURE ldexp (x : LONGREAL;
exp : INTEGER) : LONGREAL; (* gibt x mal 2 hoch exp zurück |
*) |
| Interface Stack <by
daniel> |
TYPE
stack_ptr = REF stack;
stack = RECORD
addr : INTEGER;
value : REFANY;
next,back : stack_ptr;
END;
TYPE
sort = PROCEDURE(x,y : REFANY) : BOOLEAN; (* Funktion um "groesser gleich" zweier Einträge zu bestimmen *)
show = PROCEDURE(x : REFANY); (* Funktion für die Ausgabe eines Elements*)
| PROCEDURE Push(VAR stack : stack_ptr; x
: REFANY); (* legt ein neues Element x auf den Stapel stack |
*) |
||
| PROCEDURE Pop (VAR stack : stack_ptr) :
REFANY; (* nimmt das oberste Element vom Stapel stack und gibt es zurück (Element wird dabei gelöscht!) |
RAISES {StackEmpty} |
*) |
|
| PROCEDURE Drop(VAR stack : stack_ptr); (* löscht das oberste Element vom Stapel stack |
RAISES {StackEmpty) |
*) |
|
| PROCEDURE Swap(VAR stack : stack_ptr); (* vertauscht das oberste Element mit dem darunter liegenden Element des Stapels stack |
RAISES {StackEmpty} |
*) |
|
| PROCEDURE Dub (VAR stack : stack_ptr); (* kopiert das oberste Element des Stapels stack und legt es oben drauf |
RAISES {StackEmpty} |
*) |
|
| PROCEDURE Sort(VAR stack : stack_ptr; check
: sort); (* sortiert die Elemente des Stapels stack nach den Kriterien in check |
RAISES {StackEmpty} |
*) |
|
| PROCEDURE Top (stack : stack_ptr) : REFANY; (* gibt das oberste Element des Stapels stack zurück, ohne es zu löschen |
RAISES {StackEmpty} |
*) |
|
| PROCEDURE Size(stack : stack_ptr) : CARDINAL; (* gibt die Anzahl der Elemente zurück, die auf den Stapel stack liegen |
*) |
||
| PROCEDURE Show(stack : stack_ptr; print
: show); (* gibt alle Elemente des Stapels stack mittels der Funktion print aus |
RAISES {StackEmpty} |
*) |
| Interface Input <by
daniel> |
TYPE
setofchar = SET OF CHAR;
| PROCEDURE get_int
(text : TEXT; min : INTEGER := FIRST(INTEGER); max : INTEGER := LAST(INTEGER))
: INTEGER; (* liesst einen Intwert in den Grenzen [min,max] ein, text enthält die Eingabeaufforderung |
*) |
|
| PROCEDURE get_abs_int (text : TEXT; min
: INTEGER := 0; max : INTEGER := LAST(INTEGER)) : INTEGER; (* liesst einen Intwert in den Grenzen [min,max] ein und bildet den Betrag davon, text enthält die Eingabeaufforderung |
*) |
|
| PROCEDURE get_card (text : TEXT; min :
INTEGER := 0; max : INTEGER := LAST(INTEGER)) : CARDINAL; (* liesst eine Cardinalzahl in den Grenzen [min,max] ein, text enthält die Eingabeaufforderung |
*) |
|
| PROCEDURE get_real (text : TEXT; min :
REAL := FIRST(REAL); ;max : REAL := LAST(REAL) ) : REAL; (* liesst einen Realwert in den Grenzen [min,max] ein, text enthält die Eingabeaufforderung |
*) |
|
| PROCEDURE get_longreal (text : TEXT; min
: LONGREAL := FIRST(LONGREAL); max : LONGREAL := LAST(LONGREAL)) : LONGREAL; (* liesst einen Longrealwert in den Grenzen [min,max] ein, text enthält die Eingabeaufforderung |
*) |
|
| PROCEDURE get_extended (text : TEXT; min
: EXTENDED := FIRST(EXTENDED); max : EXTENDED := LAST(EXTENDED)) : EXTENDED; (* liesst einen Extendedwert in den Grenzen [min,max] ein, text enthält die Eingabeaufforderung |
*) |
|
| PROCEDURE get_char (text : TEXT) : CHAR; (* liesst ein Zeichen ein, text enthält die Eingabeaufforderung |
*) |
|
| PROCEDURE get_achar (text : TEXT; allowed
: setofchar) : CHAR; (* liesst ein Zeichen aus der Menge allowed ein, text enthält die Eingabeaufforderung |
*) |
|
| PROCEDURE get_line (text : TEXT) : TEXT; (* liesst eine Zeile beliebiger Zeichen ein, text enthält die Eingabeaufforderung |
*) |
|
| PROCEDURE get_aline (text : TEXT; allowed
: setofchar; error_pos : BOOLEAN := FALSE) : TEXT; (* liesst eine Zeile Text ein, wobei nur Zeichen aus allowed zugelassen sind, text enthält die Eingabeaufforderung, error_pos bestimmt, ob die Position des Fehlers in der Eingabe mit angegeben wird |
*) |
| Interface Format
<by daniel> |
TYPE
Side = {left, right};
setofchar = SET OF CHAR;
| PROCEDURE fill_space (text
: TEXT; length : INTEGER; sign : CHAR; side : Side) : TEXT; (* füllt text mit Zeichen sign auf, bis der Text die Länge length hat, side bestimmt ob die Zeichen sign rechts oder links angehängt werden |
*) |
|
| PROCEDURE cut_spaces (text
: TEXT) : TEXT; (* entfernt alle Leerzeichen, die links und rechts von text vorkommen |
*) |
|
| PROCEDURE remove_char (text : TEXT;
pos : INTEGER) : TEXT; (* entfernt das Zeichen an der Stelle pos aus dem String text |
*) |
|
| PROCEDURE cut_char_all (text : TEXT; sign
: CHAR) : TEXT; (* entfernt alle Zeichen sign aus dem String text |
*) |
|
| PROCEDURE cut_all_char (text : TEXT; remove
: setofchar) : TEXT; (* entfernt alle Zeichen aus text, die in der Menge remove angegeben werden |
*) |
|
| PROCEDURE insert_char (text : TEXT; pos
: INTEGER; char : CHAR) : TEXT; (* fügt an der Stelle pos [0..Length(text)+1] in text das Zeichen char ein, der Rest wird nach Rechts geschoben |
||
| PROCEDURE change_char (text : TEXT; pos
: INTEGER; char : CHAR) : TEXT; (* ersetzt im String text das Zeichen an der Stelle pos durch das Zeichen char |
||
| PROCEDURE change_all (text : TEXT; char,
new : CHAR) : TEXT; (* ersetzt alle Zeichen char in text durch das Zeichen new |
Download |
| File |
Size |
Date |
Beschreibung |
||
| examples.tar.gz |
4132 bytes |
17-Nov-2003 |
alle
Beispiele aus der Dokumentation |
||
| stack.tar.gz |
1746 bytes |
17-Nov-2003 |
Modul
für die Simulation eines Stacks mit einem beliebigen Variablen-Typ
(siehe Referenz) |
||
| input.tar.gz |
1682 bytes |
18-Nov-2003 |
Modul zum einfachen
Einlesen von Typen mit ein paar Features =) (siehe Referenz) |
||
| format.tar.gz |
882 bytes |
18-Nov-2003 |
Modulerweiterung
zu Text - Features für Zeichen löschenn, anhängen ect. (siehe
Referenz) |
Links |
| Link |
Beschreibung |
||
| http://www.m3.org/ |
die Seite für
Modula 3 |
||
| http://www1.cs.columbia.edu/graphics/modula3/tutorial/www/m3_toc.html |
eine Umfangreiche
Referenz für Modula, allerdings nicht sehr übersichtlich |
||
| http://wwwold.ifi.uni-klu.ac.at/Modula-3/m3buch/beispiele.html |
Programmierbeispiele
für Modula - einfach immer gut =) |
||
| http://research.compaq.com/SRC/modula-3/html/srcm3.html |
SRC-Modula 3 (gute
Seite, aber viel zu klicken) |
EOF |