DOKUMENTATION zum ARIBAS Interpreter for Arithmetic, V1.0, Sep. 1996 written by 0. Forster, email [email protected]� Beilage zum Buch O. Forster: Algorithmische Zahlentheorie, Vieweg-Verlag 1996, ISBN 3-528-06580-X (*-------------------------------------------------------------*) Inhalt 1) 2) 3) 4) 5) 6) EINLEITUNG BEZEICHNER (identifier) DATENTYPEN OPERATOREN KONTROLL-STRUKTUREN FUNKTIONS-REFERENZ a) Funktionen fuer Integer-Arithmetik b) Funktionen fuer Real-Arithmetik und Analysis c) Random d) Characters, Strings e) Byte-Strings f) Arrays g) Stacks h) In-Out i) System-Funktionen 7) FUNKTIONS-DEFINITIONEN 8) VARIABLEN-DEKLARATIONEN 9) KOMANDOZEILEN-ARGUMENTE (*-------------------------------------------------------------*) 1) EINLEITUNG ============= ARIBAS ist ein interaktiver Interpreter, der vorallem fuer Ganzzahl-Arithmetik grosser Zahlen und Multipraezisions-GleitkommaArithmetik geeignet ist. Nach dem Start zeigt ARIBAS ein Prompt ==> und erwartet die Eingabe einer oder mehrerer durch ein Semicolon getrennter Anweisungen oder Ausdruecke. Der Abschluss der Eingabe erfolgt durch einen Punkt. Die Anweisungen werden dann der Reihe nach ausgefuehrt und das Ergebnis der letzten Anweisung wird nach dem Zeichen -: angezeigt. Danach erscheint wieder das Prompt-Zeichen und es kann eine neue Eingabe erfolgen. Beispiele: ==> 2*3 + 17. -: 23 ==> 2**1000. -: 10_71508_60718_62673_20948_42504_90600_01810_56140_48117_05533_60744_3750 3_ 88370_35105_11249_36122_49319_83788_15695_85812_75946_72917_55314_68251_8 7145_ 28569_23140_43598_45775_74698_57480_39345_67774_82423_09854_21074_60506_2 3711_ 41877_95418_21530_46474_98358_19412_67398_76755_91655_43946_07706_29145_7 1196_ 47768_65421_67660_42983_16526_24386_83720_56680_69376 Der Operator ** bedeutet (wie z.B. in Fortran) Potenzierung. Der Unterstrich _ wird zur Gliederung grosser ganzer Zahlen benuetzt. ==> x := 3; y := 4; z := sqrt(x*x + y*y). -: 5.00000000 Dies ist ein Beispiel fuer eine Folge von Anweisungen. ==> for k := 2 to 10 do writeln(k:3,log(k):12:6); end. 2 0.693147 3 1.098612 4 1.386294 5 1.609438 6 1.791759 7 1.945910 8 2.079442 9 2.197225 10 2.302585 Hier wurden als Nebenwirkung der writeln-Anweisungen (die FormatAngaben wie in Pascal gestatten) die natuerlichen Logarithmen der Zahlen von 2 bis 10 ausgegeben. Diese Anweisung hat ein leeres Ergebnis und ist nur wegen der Nebenwirkung interessant. ==> function fac(n: integer): integer; var x: integer; begin x := 1; while n > 1 do x := x*n; dec(n); end; return x; end. -: fac Diese Anweisung ist eine Funktions-Definition. Als Ergebnis erscheint hier noch einmal der Funktions-Name. Danach steht diese Funktion zur Benutzung zur Verfuegung: ==> fac(100). -: 933_26215_44394_41526_81699_23885_62667_00490_71596_82643_81621_46859_ 29638_95217_59999_32299_15608_94146_39761_56518_28625_36979_20827_22375_8 2511_ 85210_91686_40000_00000_00000_00000_00000 Diese Beispiele dienten nur einer ersten kurzen Vorstellung von ARIBAS. Fuer eine erste praktische Einfuehrung verweisen wir auf das Tutorial. Das folgende ist eine mehr systematische Beschreibung von ARIBAS. 2) BEZEICHNER (identifier) ========================== Bezeichner (Namen von Variablen und Funktionen) koennen aus den Buchstaben a bis z und A bis Z, den Ziffern 0 bis 9 sowie dem Unterstrich _ gebildet werden, das erste Zeichen muss ein Buchstabe oder der Unterstrich sein. Beispiele fuer zulaessige Bezeichner: x x1 _alfa1 untere_Grenze Ungueltige Bezeichner sind z.B. 1alpha, beta.2 oder gamma_$3. Ausserdem duerfen in Bezeichnern keine Umlaute oder das scharfe s enthalten sein und natuerlich keine eingebetteten Leerzeichen. Bezeichner duerfen sich nicht ueber mehrere Zeilen erstrecken. Ansonsten ist die Laenge beliebig. Alle Zeichen, sowie der Unterschied zwischen Gross- und Kleinbuchstaben sind signifikant. Die Bezeichner fuer selbstdefinierte Variable und Funktionen muessen verschieden von den Schluesselwoertern von ARIBAS und den Namen eingebauter Funktionen und Prozeduren sein. Eine Liste dieser reservierten Namen erhaelt man durch den Aufruf ==> symbols(aribas). -: (ARGV, _, __, ___, abs, alloc, and, arccos, arcsin, arctan, arctan2, aribas, array, atof, atoi, begin, binary, bit_and, bit_clear, bit_length, bit_not, bit_or, bit_set, bit_shift, bit_test, bit_xor, boolean, break, by, byte_length, byte_string, cardinal, cf_factorize, char, chr, close, concat, const, cos, create_array, dec, decode_float, div, do, double_float, else, elsif, end, even, exit, exp, extended_float, external, factor16, factorial, false, file, float, float_ecvt, floor, flush, for, frac, ftoa, function, gc, gcd, gcdx, get_filepos, get_floatprec, get_key, get_printbase, halt, help, if, inc, integer, isqrt, itoa, jacobi, length, load, load_edit, log, long_float, make_unbound, max, mem_and, mem_bclear, mem_bitswap, mem_bset, mem_btest, mem_byteswap, mem_not, mem_or, mem_shift, mem_xor, memavail, min, mod, mod_coshmult, mod_inverse, mod_pemult, new, nil, not, odd, of, open_append, open_read, open_write, or, ord, pi, pointer, prime32test, procedure, product, protocol, rab_primetest, random, random_seed, read_block, read_byte, readln, real, record, return, rewind, rho_factorize, round, save_input, set_filepos, set_floatprec, set_printbase, short_float, sin, single_float, sort, sqrt, stack, stack2array, stack_empty, stack_pop, stack_push, stack_reset, stack_top, stderr, stdin, stdout, string, string_split, substr_index, sum, symbols, system, tan, then, timer, to, tolower, toupper, true, trunc, type, user, var, while, write, write_block, write_byte, writeln) Die Zeichen _, __, ___ sind System-Variablen, die jeweils das letzte, das vorletzte und das drittletzte Ergebnis enthalten. Beispiel: ==> sqrt(2). -: 1.41421356 ==> sqrt(_). -: 1.18920711 ==> sqrt(_). -: 1.09050773 ==> _ * __ * ___. -: 1.83400808 ==> _ ** (8/7). -: 2.00000000 Hier wurden der Reihe nach die Quadratwurzel, die vierte Wurzel und die achte Wurzel aus 2 berechnet und dann miteinander multipliziert. Das Ergebnis ist 2 hoch 7/8. Dies Resultat hoch 8/7 muss also wieder 2 ergeben. (Der Potenz-Operator in ARIBAS ist **). Kommentare ---------Text, der zwischen den Zeichen (* und *) eingeschlossen ist, wird von ARIBAS als Kommentar angesehen und ueberlesen. Kommentare duerfen sich ueber mehrere Zeilen erstrecken. Ineinander geschachtelte Kommentare sind nicht zulaessig. 3) DATENTYPEN ============= In ARIBAS werden folgende Datentypen unterstuetzt: integer real boolean char string byte_string array stack file function (*----------------------------------------------------------------*) integer Der Datentyp integer umfasst die ganzen Zahlen, die in ARIBAS bis zu einer Groesse von 2**16 Binaerstellen verarbeitet werden koennen. Integer-Literale werden gegeben durch ein optionales Vorzeichen und eine nichtleere Folge von Dezimalziffern. Zur Gliederung laengerer Zahlen kann der Unterstrich _ verwendet werden. In diesem Fall muss unmittelbar vor und nach dem Unterstrich eine Ziffer stehen. Beispiele fuer Integer-Literale: 1 1234567890 -3456_78965_12367 Ausser der Dezimal-Darstellung ist auch die Darstellung mit den Basen 2, 8 oder 16 moeglich. Fuer die Basis 2 ist vor die Folge der Binaerziffern (0 und 1) die Zeichenfolge 0y zu setzen, fuer die Basis 16 vor die Folge der Hexadezimalziffern (0 ... 9, A ... F) die Zeichenfolge 0x. Die Basis 8 wird durch das Praefix 0o gekennzeichnet. Ein eventuelles Vorzeichen steht noch vor 0y, 0o bzw. 0x. Beispiele: 0y111101 0o177 0xFFFFF_FFFFE -0x123456789ABCDEF (*----------------------------------------------------------------*) real Der Datentyp real umfasst eine Computer-Approximation der reellen Zahlen. Real-Literale werden gegben durch eine endliche Dezimal-Darstellung mit einem optionalen Vorzeichen + oder -, anschliessend eine nichtleere Folge von Dezimalziffern, einem obligatorischen Dezimalpunkt und eine weitere nichtleere Folge von Dezimalziffern sowie optional eine Exponenten-Angabe, die aus dem Zeichen E und einer ganzen Zahl (mit optionalem Vorzeichen) in Dezimaldarstellung besteht. Beispiele fuer real-Literale sind 0.3 +3.1e-45 -0.00007E1000 Unzulaessig sind z.B. die Darstellungen .333 oder 333e-3. (Die damit gemeinte Zahl kann z.B. durch 0.333 oder 333.0e-3 dargestellt werden.) Intern werden Zahlen vom Typ real binaer gespeichert (woraus z.B. folgt, dass selbst eine so einfache Zahl wie 0.1 nicht exakt dargestellt werden kann). Die Genauigkeit, mit der mit Real-Zahlen gerechnet wird, haengt von der jeweils eingestellten floatprecision ab (defaultmaessig wird mit einer Mantisse von 32 Binaerstellen gearbeitet). Genaueres hierzu findet sich in der Funktions-Referenz unter set_floatprec und get_floatprec. (*----------------------------------------------------------------*) boolean Der Datentyp boolean umfasst die Wahrheitswerte false und true. Die logischen Operatoren not, and und or wirken nach den ueblichen Regeln auf boolesche Variable. Boolesche Werte entstehen z.B. auch als Ergebnisse von Vergleichs-Operatoren und werden in den Bedingungen fuer if- und while-Konstruktionen benuetzt. Ueberall, wo in ARIBAS ein boolescher Wert erwartet wird, kann auch ein integer-Wert eingegeben werden. Dabei wird (wie in der Programmiersprache C) der Wert 0 als false, und jeder Wert ungleich 0 als true interpretiert. (*----------------------------------------------------------------*) char Der Datentyp char (Character) umfasst 256 Zeichen, die nach ihrem Ascii-Code (0 bis 255) geordnet sind. Character-Literale von druckbaren Zeichen (Ascii-Code >= 32) werden durch Einschluss des Zeichens in Hochkommata gegeben, z.B. 'A'. Die Funktion chr verwandelt IntegerWerte aus dem Intervall 0 bis 255 in Characters mit dem entsprechenden Ascii-Code. Dadurch lassen sich auch Characters erzeugen, die nicht zu druckbaren Zeichen gehoeren. (*----------------------------------------------------------------*) string Der Datentyp string umfasst Folgen von Characters und dient zur Darstellung von Text. String-Literale werden durch Einschluss der Zeichenfolge in Anfuehrungszeichen " gegeben, z.B. "ABC". Auf die einzelnen Zeichen des Strings kann wie folgt zugegriffen werden: ==> s := "abcdef"; s[3]. -: 'd' Die Numerierung beginnt bei 0, das letzte Zeichen hat den Index n-1, wenn n die Laenge des Strings ist. (*----------------------------------------------------------------*) byte_string Der Datentyp byte_string ist ein Array-Typ, der alle endlichen Folgen von Bytes umfasst (koennte in Pascal etwa als packed array of byte umschrieben werden). byte_string-Literale werden in der Form $XXXXXX...XX gegeben, wobei XX fuer die hexadezimale Darstellung der Bytes (integers zwischen 0 und 255 einschliesslich) steht. Der Unterstrich _ kann zur Gliederung eingesetzt werden. Beispielsweise wird durch ==> B := $0080_12FF78. -: $0080_12FF_78 ein byte_string der Laenge 6 definiert. Auf die einzelnen Komponenten kann wie bei Strings zugegriffen werden: ==> B[1]. -: 128 ==> B[3]. -: 255 byte_strings koennen in Binaer-Dateien geschrieben und aus solchen gelesen werden (Funktionen write_block und read_block, siehe weiter unten) und dienen so dem Daten-Austausch von ARIBAS mit der Aussenwelt. Es existieren Umwandlungs-Funktionen von integers in byte_strings und umgekehrt. (*----------------------------------------------------------------*) array of Type Beim allgemeinen Array-Typ koennen die Komponenten einen beliebigen Datentyp Type haben, z.B. selbst wieder Arrays oder Strings sein. Array-Literale werden durch Aufzaehlung der Komponenten zwischen runden Klammern wie im folgenden Beispiel gegeben. vec := (37, 41, -9). Bei Arrays der Laenge 1 sind statt der runden Klammern geschweifte Klammern zu verwenden. vec1 := {37}. Der Ausdruck (37) wuerde einfach als die Zahl 37 interpretiert. Auch bei Arrays der Laenge > 1 duerfen geschweifte Klammern verwendet werden. (*----------------------------------------------------------------*) stack In ARIBAS gibt es einen vordefinierten Datentyp stack mit den Funktionen stack_push, stack_pop, stack_top, stack_rest, stack_empty stack2array und length (siehe weiter unten in der Funktions-Referenz). Ein stack kann z.B. guenstig eingesetzt werden zur Zwischenspeicherung von Objekten (beliebigen Datentyps), deren Anzahl a priori noch unbekannt ist (etwa die Primfaktoren einer ganzen Zahl). (*----------------------------------------------------------------*) file Datentyp file: ARIBAS unterstuetzt Text-Files und Binaer-Files. Siehe Funktions-Referenz unter In-Out. (*----------------------------------------------------------------*) function Datentyp function: Benutzerdefinierte und eingebaute Funktionen (ausser write und writeln) koennen Variablen zugewiesen werden und als Argumente anderen Funktionen uebergeben werden. Beispiel: ==> F := (cos,sin,tan). -: (cos, sin, tan) ==> for i := 0 to length(F)-1 do fun := F[i]; writeln(fun(pi/6)); end. 0.866025404 0.500000000 0.577350269 (*----------------------------------------------------------------*) Selbstdefinierte Datentypen: Ausser den eingebauten Datentypen koennen in ARIBAS neue Datentypen definiert werden. Dazu stehen records und pointer zur Verfuegung (noch undokumentiert). (*----------------------------------------------------------------*) 4) OPERATOREN ============= Der Zuweisungs-Operator := -------------------------Eine Zuweisung hat die Gestalt <variable> := <expression> Dabei wird der Ausdruck <expression> berechnet. Das Resultat wird der Variablen <variable> zugewiesen. Im allgemeinen ist <variable> ein einfacher Bezeichner, kann aber auch ein Array-Element oder ein Subarray sein. Der Datentyp von <expression> muss zuweisungskompatibel zum Datentyp von <variable> sein. Auf dem Top-Level (d.h. ausserhalb von Funktionsdefinitionen) kann <variable> auch ein noch nicht deklarierter ungebundener Bezeichner sein. Durch die Zuweisung wird der Bezeichner dann implizit als Variable des passenden Datentyps deklariert. Die Zuweisung als Ganzes ist ein Ausdruck, dessen Wert gleich dem Wert von <expression> ist. Der Zuweisungs-Operator ist (wie in C) rechts-assoziativ, so dass Mehrfach-Zuweisungen moeglich sind, wie <variable1> := <variable2> := <expression> Bei Zuweisungen von Arrays (etwa vec1 := vec2) wird stets eine neue Kopie (hier von vec2) gefertigt (es handelt sich nicht, wie etwa in C, um eine Zuweisung von Pointern). Arrays verschiedener Laenge sind zuweisungs-kompatibel. Beispiel: ==> vec1 := (1,2,3). -: (1, 2, 3) ==> vec2 := (4,5,6,7). -: (4, 5, 6, 7). Jetzt ist die Zuweisung vec1 := vec2 moeglich: ==> vec1 := vec2. -: (4, 5, 6, 7). Wird danach vec2 veraendert, ==> vec2[2] := -1. -: -1 so bleibt vec1 davon unberuehrt. ==> vec2. -: (4, 5, -1, 7) ==> vec1. -: (4, 5, 6, 7) Arithmetische Operatoren -----------------------+, -, *, **, /, div, mod + und - sind sowohl unaere Operatoren (als Praefix) als auch binaere Operatoren (Infix). Als binaere Operatoren sind sie links-assoziativ. Die Operanden duerfen integers oder reals sein. Ist einer der Operanden ein integer, der andere ein real, so erfolgt eine implizite Umwandlung des integers in den Typ real. * bezeichnet die Multiplikation und ist ein binaerer links-assoziativer Infix-Operator, der ebenfalls auf integers und reals anwendbar ist. ** ist der Potenzierungs-Operator. Er ist binaer und rechts-assoziativ. Die Operanden koennen integers oder reals sein. Ist in dem Ausdruck x ** y die Basis x ein integer und der Exponent y eine nicht-negative ganze Zahl, so ist das Resultat wieder ein integer; in allen anderen Faellen ein real. Ist der Exponent negativ, so muss die Basis ungleich 0 sein. Ist der Exponent ein real, so muss die Basis positiv sein. Der binaere, links-assoziative Operator / bezeichnet die Division. Die Operanden koennen integers oder reals sein; das Resultat ist stets ein real. Division durch 0 ist nicht zugelassen. div und mod sind binaere, links-assoziative Operatoren, die nur auf integers anwendbar sind und als Resultat einen integer liefern. x div y ist die groesste ganze Zahl kleiner-gleich x/y. Der Operator mod ist durch die Gleichung x = (x div y) * y + (x mod y) definiert. Der Divisor y muss ungleich 0 sein. Vergleichs-Operatoren --------------------=, /= oder <>, <, <=, >, >= Alle Vergleichs-Operatoren sind binaere Infix-Operatoren und liefern einen Booleschen Wert (true oder false) als Ergebnis. Die Operatoren = (gleich) und /=, <> (zwei Synonyme fuer ungleich) koennen auf Operanden beliebigen Typs angewendet werden. Mit den Operatoren < (keiner), <= (kleiner-gleich), > (groesser), >= (groesser-gleich) koennen Zahlen (integer oder real) miteinander verglichen werden, ausserdem Characters und Strings. In den letzten beiden Faellen erfolgt der Vergleich anhand des Ascii-Codes. Boolesche Operatoren -------------------not, and, or not ist ein unaerer Praefix-Operator, and und or sind binaere InfixOperatoren, die auf Boolesche Ausdruecke anwendbar sind. Die Auswertung der Argumente von and und or erfolgt von links nach rechts und wird abgebrochen, sobald das Resultat feststeht. Damit ist dann ein Ausdruck wie u > 0 and v/u < 1 zulaessig, der einen Fehler erzeugen wuerde, falls u = 0 ist und immer beide Argumente des and-Operators ausgewertet wuerden. Vorrang von Operatoren ---------------------Bei der Bildung von zusammengesetzten Ausdruecken koennen Klammern eingespart werden, wenn man den Vorrang von Operatoren beruecksichtigt. Sie sind wie folgt nach absteigender Bindungskraft in Klassen eingeteilt: 1. 2. 3. 4. 5. 6. 7. 8. Potenz-Operator Unaeres Minus und Plus Multiplikations- und Divisions-Operatoren *, /, div, mod Binaeres Plus und Minus Vergleichs-Operatoren Boolescher Operator not Boolesche Operatoren and und or Zuweisungs-Operator Beispiel: bvar := not x < y and y < z bedeutet dasselbe wie bvar := ((not (x < y)) and (y < z)) (*-----------------------------------------------------------*) 5) KONTROLL-STRUKTUREN ====================== if then elsif else end while do end for to by do end break (*-----------------------------------------------------------------*) if <bool expr> then <statement-list> elsif <bool expr> then <statement-list> else <statement-list> end; Es koennen auch mehrere (oder null) elsif-Teile vorkommen. Der else-Teil kann auch fehlen. Beispiel: function sign(x: real): integer; begin if x > 0 then return 1; elsif x < 0 then return -1; else return 0; end; end; *-----------------------------------------------------------------*) while <boolean expr> do <statement-list> end; Ist der Boolesche Ausdruck <boolean expr> wahr, wird die Anweisungsfolge <statement-list> ausgefuehrt (diese kann den Wert von <boolean expr> aendern). Ist danach <boolean expr> immer noch wahr, wird <statement-list> erneut ausgefuehrt. Dies wird solange wiederholt, bis <boolean expr> falsch wird, oder die while-Schleife durch ein return-statement oder break-statement verlassen wird. *-----------------------------------------------------------------*) for Laufvar := Start to End do <statement-list> end; Laufvar muss eine Integer-Variable sein, Start und End Integer-Ausdruecke. Diese werden beim 1. Betreten der for-Schleife ausgewertet. Dann wird die <statement-list> der Reihe nach fuer die Start, Start+1, ..., End ausgefuehrt. Falls End < Start, wird die <statement-list> ueberhaupt nicht ausgefuehrt. for Laufvar := Start to End by Incr do <statement-list> end; Hier ist zusaetzlich ein Integer-Ausdruck Incr gegeben, der ebenfalls zu Beginn ausgewertet wird und einen Wert /= 0 ergeben muss. Ist Incr > 0 und End >= Start, wird die <statement-list> fuer Start + k*Incr, k=0,1,..., ausgefuehrt, solange Start + k*Incr <= End. Ist Incr < 0 und End, wird die <statement-list> ausgefuehrt fuer Start + k*Incr, k=0,1,..., ausgefuehrt, solange Start + k*Incr >= End. Beispiel: ==> for k := 11 to 0 by -2 do write(k,"; "); end. erzeugt die Ausgbabe 11; 9; 7; 5; 3; 1; *-----------------------------------------------------------------*) break Der Befehl break bewirkt (wie in der Programmiersprache C) ein unmittelbares Verlassen einer while- oder for-Schleife. Beispiel: ==> for x := 10**7+1 to 10**8 by 2 do if factor16(x) = 0 then break; end; end; x. -: 10000019 *-----------------------------------------------------------------*) Es gibt in ARIBAS kein repeat .. until Diese Kontrollstruktur kann man immer durch geeignete while-Konstruktionen ersetzen. *-----------------------------------------------------------------*) 6) FUNKTIONS-REFERENZ ===================== a) Funktionen fuer Integer-Arithmetik ===================================== set_printbase get_printbase sum product odd even abs max min inc dec gcd gcdx isqrt factorial mod_coshmult mod_pemult mod_inverse jacobi factor16 prime32test rab_primetest rho_factorize cf_factorize (*----------------------------------------------------------------*) set_printbase(b: integer): integer; b muss eine der Zahlen 2, 8, 10, 16 sein. Die Funktion bewirkt, dass nachfolgende Ausgaben von Integern in der Basis b erfolgen. Das Funktionsergebnis ist die neu eingestellte printbase. (Falls b nicht zulaessig ist, bleibt die bisherige Einstellung unveraendert.) (*----------------------------------------------------------------*) get_printbase(): integer; Liefert als Funktions-Ergebnis die derzeit gueltige printbase. (*----------------------------------------------------------------*) sum(vec: array of integer): integer; sum(vec: array of real): real; product(vec: array of integer): integer; product(vec: array of real): real; Liefert die Summe bzw. das Produkt aller Komponenten von vec. (*----------------------------------------------------------------*) even(x: integer): boolean; odd(x: integer): boolean; Stellt fest, ob x gerade bzw. ungerade ist. (*----------------------------------------------------------------*) max(x1,...,xn: integer): integer; max(x1,...,xn: real): real; min(x1,...,xn: integer): integer; min(x1,...,xn: real): real; Ergibt das Maximum (Minimum) der Argumente x1,...,xn. (*----------------------------------------------------------------*) max(vec: array of integer): integer; max(vec: array of real): real; Ergibt das Maximum (Minimum) der Komponenten von vec. (*----------------------------------------------------------------*) inc(var x: integer [; delta: integer]): integer; Erhoeht die Integer-Variable x um delta (Defaultwert delta = 1). Rueckgabewert ist der neue Wert von x. (*----------------------------------------------------------*) dec(var x: integer [; delta: integer]): integer; Vermindert die Integer-Variable x um delta (Defaultwert delta = 1). Rueckgabewert ist der neue Wert von x. (*----------------------------------------------------------*) gcd(x1,...,xn: integer): integer; Liefert den groessten gemeinsamen Teiler der Zahlen x1,x2,...,xn. Fuer n = 1 ist gcd(x) = x; fuer n = 0 ergibt sich gcd() = 0. (*----------------------------------------------------------*) gcdx(x,y: integer; var u,v: integer): integer; Liefert den groessten gemeinsamen Teiler d von x und y. Gleichzeitig werden in den Variablen u und v Werte abgelegt, so dass d = u*x + v*y (*----------------------------------------------------------*) mod_inverse(x, mm: integer): integer; Liefert das Inverse von x modulo mm, falls x zu mm teilerfremd ist. Andernfalls wird 0 zurueckgegeben; Beispiele: ==> mod_inverse(17,100). -: 53 ==> mod_inverse(18,100). -: 0 (*----------------------------------------------------------*) isqrt(x: integer): integer; Liefert fuer eine ganze Zahl x >= 0 die groesste ganze Zahl y mit y*y <= x. (*----------------------------------------------------------*) factorial(n: integer): integer; Das Argument n muss eine nicht-negative ganze Zahl sein. Berechnet n! (n Fakultaet). Beispiel: ==> factorial(8). -: 40320 (*----------------------------------------------------------*) mod_coshmult(x,s,mm: integer): integer; Ist x eine ganze Zahl und xi so gewaehlt, dass cosh(xi) = x, so ist fuer jede natuerliche Zahl s auch cosh(s*xi) eine ganze Zahl. Diese Zahl modulo mm ist das Funktions-Ergebnis. Das Funktions-Ergebnis kann auch durch folgende rekursiv definierte (Lucas-)Folge ermittelt werden: a(0) = 1; a(1) = x; a(k+2) = 2*x*a(k+1) - a(k); Dann ist a(s) mod mm das Funktions-Ergebnis. (*----------------------------------------------------------*) mod_pemult(x,s,a,mm: integer): array[2] of integer; Sei pe die Weierstrasssche pe-Funktion zur elliptischen Kurve E(a) y*y = x*x*x + a*x*x + x Sei xi ein Punkt der Kurve mit pe(xi) = x. Dann ist s*xi wieder ein Kurvenpunkt von E(a) (bzgl. der Struktur von E(a) als abelsche Gruppe), so dass entweder s*xi eine Polstelle von pe ist oder pe(s*xi) = u/v eine rationale Zahl ist. (Es werden u und v als teilerfremd angenommen.) Ist im zweiten Fall v teilerfremd zu mm, so ist das Funktions-Ergebnis (z,1), wobei z = u/v mod mm. Haben v und mm einen gemeinsamen Teiler d > 1, so ist das Funktions-Ergebnis (d,0). Ist schliesslich s*xi ein Pol von pe, so ist das Funktions-Ergebnis (mm,0). Diese Funktion ist nuetzlich fuer die Faktorisierung ganzer Zahlen mittels elliptischer Kurven. (*----------------------------------------------------------*) factor16(x [,x0 [,x1]]: integer): integer; Stellt fest, ob die Zahl x einen Primteiler < min(2**16,x) hat. Das Funktionsergebnis von factor16(x) ist der kleinste solche Primteiler, falls es ihn gibt, sonst 0. Sind die optionalen Argumente x0 bzw. x0 und x1 angegeben, so werden nur Primteiler p mit p >= x0 bzw. x0 <= p <= x1 gesucht. (*----------------------------------------------------------*) prime32test(x: integer): integer; Testet, ob x eine Primzahl < 2**32 ist. (Ein eventuelles Vorzeichen von x wird ignoriert.) Ist dies der Fall, wird 1 zurueckgegeben. Ist abs(x) < 2**32, aber keine Primzahl, ist das Resultat 0. Falls abs(x) >= 2**32, wird -1 zurueckgegeben. (*----------------------------------------------------------*) rab_primetest(x: integer): boolean; Fuehrt den Rabinschen Pseudo-Primzahltest fuer die Zahl x aus. Ist das Ergebnis false, so ist x sicher keine Primzahl. Eine Zahl x, fuer die factor16(x) = 0 und rab_primetest(x) = true, ist mit grosser Wahrscheinlichkeit eine Primzahl. Zur Erhoehung der Wahrscheinlichkeit kann der Test wiederholt werden. (*----------------------------------------------------------*) jacobi(a,m: integer): integer; Liefert das Jacobi-Symbol von a ueber m. Der Modul m muss eine ungerade Zahl sein; a kann eine beliebige ganze Zahl sein, der Wert der Jacobi-Funktion haengt aber nur von a mod m ab. Sind a und m nicht teilerfremd, ist das Resultat 0, sonst +1 oder -1. Ist p eine Primzahl und a mod p /= 0, so ist jacobi(a,p) genau dann gleich +1, falls a quadratischer Rest modulo p ist. (*----------------------------------------------------------*) rho_factorize(x:integer [; b: integer]): integer; Versucht, die Zahl x nach dem Pollardschen rho-Algorithmus zu faktorisieren. Das optionale Argument b ist eine Schranke fuer die maximale Anzahl der Schritte; wird es nicht angegeben, so wird defaultmaessig b = 2**16 - 1 gesetzt. Findet der Algorithmus einen Faktor, so wird dieser zurueckgegeben; bei Misserfolg ist das Ergebnis 0. Unter unguenstigen Umstaenden kann das FunktionsErgebnis auch x sein. Damit das Verfahren gut funktioniert, sollte x frei von kleinen Primfaktoren (z.B. < 1000) sein. Hat x dann einen Primfaktor p, so wird dieser im allgemeinen gefunden, wenn b > sqrt(p) ist. Ist das Funktions-Ergebnis y > 1 und < x, so ist y sicher ein Faktor von x, aber nicht notwendig prim. (*----------------------------------------------------------*) cf_factorize(x: integer [; m: integer]): integer; Versucht, x nach dem Kettenbruch-Algorithmus von Brillhart-Morrison zu faktorisieren. Die Laufzeit haengt nicht von der Groesse der Primfaktoren von x ab. (Ist also bekannt, dass x kleine Primfaktoren besitzt, sollte besser ein anderes Faktorisierungs-Verfahren benutzt werden.) Unter unguenstigen Umstaenden (z.B. wenn die Kettenbruch-Entwicklung von sqrt(x) eine zu kurze Periode hat), scheitert das Verfahren. Fuer diesen Fall kann man cf_factorize mit einem 2. Argument m, das eine ganze Zahl 1 <= m < 1024 sein muss, aufrufen. Es wird dann die Kettenbruch-Enwicklung von sqrt(m*x) benutzt. (*----------------------------------------------------------*) Bit-Operationen fuer ganze Zahlen bit_test bit_set bit_clear bit_shift bit_not bit_and bit_or bit_xor bit_length byte_length Die Bit-Operationen koennen auf alle Integers, positiv oder negativ angewendet werden. Dabei werden die negativen Integers in Zweier-Komplement-Darstellung gedacht, wobei sich das Vorzeichen-Bit links nach unendlich erstreckt. Beispielsweise hat die Zahl -1 das Bit-Muster ......11111111111111111111111111111111 (*----------------------------------------------------------------*) bit_test(x,n: integer): integer; Liefert 1, wenn in x das Bit an Position n gesetzt ist, sonst 0. Die Position wird von 0 an gezaehlt. Beispielsweise gilt bit_test(x,0) = 1 genau dann, wenn x ungerade ist. (*----------------------------------------------------------------*) bit_set(x,n: integer): integer; Setzt in x das Bit in Position n auf 1. (*----------------------------------------------------------------*) bit_clear(x,n: integer): integer; Setzt in x das Bit in Position n auf 0. (*----------------------------------------------------------------*) bit_shift(x,n: integer): integer; Die Zahl n kann positiv oder negativ sein. Fuer n >= 0 bedeutet bit_shift(x,n) einen Shift der Bit-Darstellung von x um n Stellen nach links; dies ist gleichbedeutend mit einer Multiplikation mit 2**n. Fuer n < 0 ist ein Shift um abs(n) Stellen nach rechts; gleichbedeutend mit x div 2**abs(n). (*----------------------------------------------------------------*) bit_not(x: integer): integer; Invertiert alle Bits von x. Gleichbedeutend mit -x-1. (*----------------------------------------------------------------*) bit_and(x,y: integer): integer; bit_or(x,y: integer): integer; bit_xor(x,y: integer): integer; Bitweises und, oder bzw. exklusives oder von x und y. Beispielsweise ist bit_and(x,3) aequivalent mit x mod 4. (*----------------------------------------------------------------*) bit_length(x: integer): integer; Liefert die kleinste natuerliche Zahl n, so dass abs(x) < 2**n (*----------------------------------------------------------------*) byte_length(x: integer): integer; Liefert die kleinste natuerliche Zahl n, so dass abs(x) < (2**8)**n, d.h. die Anzahl der Bytes, die zur binaeren Darstellung von abs(x) noetig sind. (*----------------------------------------------------------------*) b) Funktionen fuer Real-Arithmetik und Analysis =============================================== round floor trunc frac set_floatprec get_floatprec decode_float float sqrt exp log sin cos tan arctan arctan2 arcsin arccos pi (*----------------------------------------------------------------*) floor(x: real): integer; Ergibt die groesste ganze Zahl <= x. Beispiele: ==> floor(pi). -: 3 ==> floor(-pi). -: -4 (*----------------------------------------------------------------*) trunc(x: real): integer; Fuer x >= 0 identisch mit floor(x). Falls x < 0, wird die kleinste ganze Zahl n >= x zurueckgegeben. Beispiele: ==> trunc(pi). -: 3 ==> trunc(-pi). -: -3 (*----------------------------------------------------------------*) frac(x: real): real; Definiert durch die Gleichung x = trunc(x) + frac(x) Beispiel: ==> frac(1.23). -: 0.230000000 ==> frac(-1.23). -: -0.230000000 (*----------------------------------------------------------------*) round(x: real): integer; Rundet x auf die naechste ganze Zahl n. Ist x genau in der Mitte zwischen zwei ganzen Zahlen, wird zur geraden Zahl gerundet. Beispiele: ==> round(pi). -: 3 ==> round(3.5). -: 4 (*----------------------------------------------------------------*) set_floatprec(bb: integer): integer; set_floatprec(Floattype): integer; Diese Funktion dient zum Einstellen der Genauigkeit (in bits) mit der anschliessend mit reals gerechnet wird. Es stehen folgende Moeglichkeiten fuer Floattype zur Verfuegung: short_float: 17 bits single_float: 32 bits double_float: 64 bits long_float: 128 bits extended_float: 192 bits Als Argument ist entweder das entsprechende Symbol oder ein Integer bb einzugeben. Falls die Zahl bb nicht unter den zur Verfuegung stehenden Zahlen vorkommt, wird auf die naechst groessere gerundet. Rueckgabewert ist die neu eingestellte Genauigkeit. Beispiel: ==> set_floatprec(long_float). -: 128 ==> exp(pi*sqrt(163)). -: 262537412640768743.999999999999250073 ==> 2**0.5. -: 1.41421356237309504880168872420969808 (*----------------------------------------------------------------*) get_floatprec(): integer; get_floatprec(x: real): integer; In der ersten Form (ohne Argument) liefert die Funktion die derzeit eingestellte Genauigkeit fuer Float-Berechnungen (in bits, also eine der Zahlen 17, 32, 64, 128, 192). Ist das Argument eine Real-Zahl x, so wird die Genauigkeit, mit der diese Zahl gespeichert ist, zurueckgegeben. Beispiel: ==> set_floatprec(50). -: 64 ==> get_floatprec(pi). -: 192 ==> get_floatprec(1/3). -: 64 (*----------------------------------------------------------------*) decode_float(x: real): array[2] of integer; Fuer eine Real-Zahl x liefert decode_float(x) einen Vektor (mant, expo), der die interne Darstellung von x wiedergibt. Dabei sind mant und expo ganze Zahlen, so dass x = mant * 2**expo Beispiel: ==> set_printbase(16). -: 0x10 ==> decode_float(-1/3). -: (-0xAAAA_AAAA, -0x21) (*----------------------------------------------------------------*) float(n: integer): real; Verwandelt den Integer n in eine real-Zahl (mit der aktuellen float precision). x := float(n) ist gleichbedeutend mit x := n + 0.0. (*----------------------------------------------------------------*) pi Die Konstante pi ist auf 192 Binaerstellen genau gespeichert. Ein Ausdruck wie pi/3 wird jedoch i.a. nicht mit dieser Genauigkeit, sondern mit der gerade eingestellten Float-Genauigkeit (Default-Wert 32 Binaerstellen). Diese kann mit der Funktion set_floatprec veraendert werden. (*----------------------------------------------------------------*) Die Funktionen sqrt (Quadratwurzel), exp, log (natuerlicher Logarithmus), sin, cos, tan, arctan, arcsin, arccos sind Funktionen, die jeweils ein real-Argument erwarten und einen real-Wert als Ergebnis haben. Wird als Argument ein integer uebergeben, wird dieser automatisch in einen real-Wert verwandelt. Beispiel: ==> log(2). -: 0.693147180 ==> set_floatprec(extended_float). -: 192 ==> log(2). -: 0.693147180559945309417232121458176568075500134360255254 (*----------------------------------------------------------------*) arctan2(y,x: real): real; Die Zahlen x,y duerfen nicht gleichzeitig 0 sein. Das Ergebnis ist ein Winkel phi mit -pi < phi <= pi, fuer den gilt x = r * cos(phi); y = r * sin(phi); wobei r = sqrt(x*x + y*y). Fuer x > 0 gilt arctan2(y,x) = arctan(y/x). (*----------------------------------------------------------------*) c) Random ========= random random_seed (*----------------------------------------------------------*) random(n: integer): integer; Erzeugt eine (Pseudo-) Zufallszahl z mit 0 <= z < n. (*----------------------------------------------------------*) random(x: real): real; Erzeugt eine reelle (Pseudo-) Zufallszahl z mit 0 <= z < x. (*----------------------------------------------------------*) random_seed([s: integer]): integer; random_seed ohne Argument liefert den gegenwaertigen Zustand des Zufalls-Generators (eine ganze Zahl z mit 2**48 <= z < 2**49). Mit einem ganzzahligen Wert s als Argument kann der Zufalls-Generator auf einen bestimmten Wert gesetzt werden (das Argument s wird modulo 2**48 auf eine Zahl im Intervall 2**48 <= z < 2**49 normiert.) Dadurch lassen sich fuer Test-Zwecke reproduzierbare Werte der random-Funktion erzeugen. (*----------------------------------------------------------*) d) Characters, Strings ====================== chr ord length concat toupper tolower string_split substr_index itoa ftoa float_ecvt atoi atof (*----------------------------------------------------------*) chr(n: integer): char; ord(ch: char): integer; Die Funktion chr erzeugt den Character mit Ascii-Code n; ord ist die Umkehrfunktion von chr. (*----------------------------------------------------------*) length(s: string): integer; Ergibt Laenge des Strings s. (Die Funktion length ist auch auf Byte-Strings, Arrays, Stacks und Files anwendbar.) (*----------------------------------------------------------*) concat(arg0, arg1, ... , argn): string; Die Funktion concat erwartet ein oder mehr Argumente, die Strings oder Characters sein koennen. Es wird ein String erzeugt, der durch Hintereinanderschreiben der Argumente entsteht. Beispiel: ==> concat("create",'_',"string"). -: "create_string" (*----------------------------------------------------------*) toupper(str: string): string; toupper(ch: character): character; Verwandelt einen String bzw. einen Character in Grossbuchstaben. Dabei werden nur Characters zwischen 'A' und 'Z' betroffen; alle andern (z.B. auch Umlaute) bleiben unveraendert. (*----------------------------------------------------------*) tolower(str: string): string; tolower(ch: character): character; Verwandelt einen String bzw. einen Character in Kleinbuchstaben. Dabei werden nur Characters zwischen 'A' und 'Z' betroffen; alle andern bleiben unveraendert. (*----------------------------------------------------------*) string_split(str: string [; sep: string]): array of string; Spaltet den String str auf und gibt einen Vektor der Bestandteile zurueck. Als Trenner werden die im String sep enthaltenen Characters angesehen. Defaultmaessig sind Trenner SPACE, TAB und NEWLINE Beispiele: ==> string_split("abc def"). -: ("abc", "def") ==> string_split("abc def;xxx=yyy",";= "). -: ("abc", "def", "xxx", "yyy") (*----------------------------------------------------------*) substr_index(str, str1: string): integer; Untersucht, ob der String str1 in str als Teilstring enthalten ist und gibt die Position zurueck (Zaehlung beginnt bei 0). Ist str1 nicht in str enthalten, wird -1 zurueckgegeben. (*----------------------------------------------------------*) itoa(x: integer [; base: integer]): string; Verwandelt die ganze Zahl x in einen String. Optional kann noch die Basis base vorgeschrieben werden, die einen der Werte 2,8,10,16 annehmen darf. (*----------------------------------------------------------*) ftoa(x: real): string; Verwandelt eine reelle Zahl in einen String. Beispiele: ==> ftoa(1/239). -: "0.00418410042" ==> ftoa(pi*10**100). -: "3.14122626E100" (*----------------------------------------------------------*) atoi(s: string [; var code: integer]): integer; Verwandelt einen String, der eine ganze Zahl darstellt, in eine ganze Zahl. Wird die Funktion mit einem zweiten variable-Parameter code aufgerufen, so wird in code eine ganze Zahl abgelegt, welche i.a. die Laenge von s ist. Ist code < length(s), so stellt nur der vom Anfang an gerechnete Teilstring der Laenge code eine ganze Zahl dar. Insbesondere bedeutet code = 0 einen unzulaessigen String. Beispiele: ==> atoi("1234"). -: 1234 ==> atoi("0x1234"). -: 4660 ==> atoi("-1234 5678",len). -: -1234 ==> len. -: 5 ==> atoi("_1234",len). -: 0 ==> len. -: 0 (*----------------------------------------------------------*) atof(s: string [; var code: integer]): real; Verwandelt einen String, der eine reelle Zahl darstellt, in eine reelle Zahl. Ein zweiter, optionaler variable-Parameter code hat eine analoge Bedeutung wie bei der Funktion atoi. (*----------------------------------------------------------*) float_ecvt(x: real; ndig: integer; var decpos, sign: integer): string; Verwandelt die Zahl x in einen String mit ndig Ziffern. Der String enthaelt ausschliesslich Ziffern. Die Position des Dezimalpunkts relativ zum String-Anfang wird im Variablen-Paramater decpos zurueckgegeben (negative Werte bedeuten: Dezimalpunkt links vom String-Anfang), das Vorzeichen im Variablen-Parameter sign (sign = 0 bedeutet x >= 0, sign < 0 bedeutet x < 0). float_ecvt ist analog zur unter Unix verfuegbaren C-Funktion ecvt. Beispiel: ==> float_ecvt(pi,10,decpos,sign). -: "3141592654" ==> decpos. -: 1 ==> sign. -: 0 (*----------------------------------------------------------*) e) Byte-Strings =============== length byte_string cardinal integer hex_string text_string (*----------------------------------------------------------*) length(b: byte_string): integer; Laenge des Byte-Strings b. (*----------------------------------------------------------*) cardinal(b: byte_string): integer; Verwandelt einen byte_string in einen (nicht-negativen) integer. Die Komponenten des byte_strings werden dabei als Ziffern der ganzen Zahl bzgl. der Basis 256 interpretiert (low byte first). Der Wert der Funktion ist also gleich sum(b[i] * 256**i: 0 <= i < length(b)). (*----------------------------------------------------------*) integer(b: byte_string): integer; Verwandelt einen byte_string in einen integer. Die Komponenten des byte_strings werden dabei als Ziffern der ganzen Zahl bzgl. der Basis 256 in Zweier-Komplement-Darstellung interpretiert. Ist len = length(b) und das hoechstwertige Bit von b[len-1] nicht gesetzt, so gilt integer(b) = cardinal(b). Ist dagegen das hoechstwertige Bit von b[len-1] gesetzt, so ist das Funktions-Ergebnis eine negative Zahl mit integer(b) = cardinal(b) - 256**len. (*----------------------------------------------------------*) byte_string(x: integer [; len: integer]): byte_string; Verwandelt die ganze Zahl x in einen byte_string und ist die Umkehrfunktion von integer(). Wird das Argument len nicht angegeben, so wird als Default-Wert len = byte_length(x) angenommen. Ist len > byte_length(x), so werden die ueberzaehligen Bytes fuer x >= 0 mit 0, fuer x < 0 mit 0xFF gefuellt. Falls len < byte_length(x) ist, wird der entstehende byte_string entsprechend gekuerzt. Beispiele: ==> set_printbase(16). -: 0x10 ==> x := 65111. -: 0xFE57 ==> byte_string(x). -: $57FE ==> byte_string(-x). -: $A901 ==> byte_string(x,4). -: $57FE_0000 ==> byte_string(-x,4). -: $A901_FFFF ==> byte_string(x,1). -: $57 (*----------------------------------------------------------*) byte_string(s: string): byte_string; Verwandelt einen gewoehnlichen String in einen byte_string. Die Komponenten des resultierenden byte_strings sind die Ascii-Codes der Characters von s. Beispiel: ==> byte_string("string"). -: $7374_7269_6E67 (*----------------------------------------------------------*) string(b: byte_string): string; Verwandelt einen byte_string in einen gewoehnlichen String; Umkehrfunktion von hex_string. Der byte_string darf keine Nullen als Komponenten enthalten. Vorsicht ist ausserdem geboten, wenn Komponenten des byte_strings Codes von nicht-druckbaren Zeichen sind, die bei der Ausgabe auf den Bildschirm als Steuerzeichen interpretiert werden koennen. (*----------------------------------------------------------*) Bit-Operationen fuer Byte-Strings --------------------------------mem_btest mem_bset mem_bclear mem_not mem_and mem_or mem_xor mem_shift mem_bitswap mem_byteswap Alle diese Bit-Operationen, mit Ausnahme von mem_btest, veraendern das als Variablen-Parameter uebergebene erste Byte-String-Argument und geben den modifizierten Byte-String zurueck. Der Rueckgabewert ist nur im interaktiven Modus interessant. (*----------------------------------------------------------*) mem_btest(var b: byte_string; n: integer): integer; Liefert 1 oder 0, je nach dem das Bit an Position n (Zaehlung beginnt bei 0) des Byte-Strings b gesetzt ist oder nicht. (*----------------------------------------------------------*) mem_bset(var b: byte_string; n: integer): byte_string; Setzt das Bit an Position n im Byte-String b und gibt das modifizierte b zurueck. (*----------------------------------------------------------*) mem_bclear(var b: byte_string; n: integer): byte_string; Loescht das Bit an Position n im Byte-String b und gibt das modifizierte b zurueck. (*----------------------------------------------------------*) mem_not(var b: byte_string): byte_string; Bildet das bitweise not des Byte-Strings b und gibt das modifizierte b zurueck. (*----------------------------------------------------------*) mem_and(var b1,b2: byte_string): byte_string; mem_or(var b1,b2: byte_string): byte_string; mem_xor(var b1,b2: byte_string): byte_string; Das erste Byte-String-Argument b1 wird durch das bitweise and (bzw. or, bzw. xor) von b1 und b2 ersetzt. Der so modifizierte Byte-String b1 wird zurueckgegeben. Die Byte-Strings b1 und b2 sollen dieselbe Laenge haben. (*----------------------------------------------------------*) mem_shift(var b: byte_string; n: integer): byte_string; Fuehrt in b einen Bit-Shift um abs(n) Stellen durch. Ist n > 0, in Richtung least-significant --> most-significant, fuer n < 0 in umgekehrter Richtung. n Bits gehen verloren, die frei werdenden Stellen werden mit Nullen besetzt. Beispiel: ==> bb := $ABCD; mem_shift(bb,4). -: $B0DA (*----------------------------------------------------------*) mem_bitswap(var b: byte_string): byte_string; Vertauscht die Bits innerhalb aller Bytes von b (most significant --> least significant) und gibt den modifizierten Byte-String zurueck. (*----------------------------------------------------------*) mem_byteswap(var b: byte_string; wordlen: integer): byte_string; Der Byte-String b wird in Gruppen von je wordlen Bytes unterteilt. Innerhalb jeder Gruppe werden die Bytes vertauscht (most significant --> least significant). Rueckgabewert: Der modifizierte Byte-String. (*----------------------------------------------------------*) f) Arrays ========= length sort sum product alloc create_array (*----------------------------------------------------------*) length(vec: array): integer; Liefert die Laenge des Arrays vec. (*----------------------------------------------------------*) Die Komponenten eines Arrays vec sind als vec[i] wobei 0 <= i <= length(vec), zugaenglich. Es gibt nicht nur die Moeglichkeit, auf einzelne Komponenten eines Arrays zuzugreifen, sondern auch auf Subarrays. Ist etwa vec ein Array, so bezeichnet vec[n1..n2] den Teil von vec, der aus allen Komponenten vec[i] mit n1 <= i <= n2 besteht. Beispiel: ==> vec := (1,2,3,4,5,6,7,8,9,10). -: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) ==> vec[2..6]. -: (3, 4, 5, 6, 7) Die obere Grenze kann auch weggelassen werden; vec[n1..] bedeutet dasselbe wie vec[n1..length(vec)-1]. Subarrays koennen auch auf der linken Seite von Zuweisungen stehen und erlauben so die simultane Modifikation von mehreren Komponenten. Setzt man etwa mit dem obigen Array vec ==> vec[2..6] := (0,-1,-2,-3,-4). -: (0,-1,-2,-3,-4) so wird ==> vec. -: (1, 2, 0, -1, -2, -3, -4, 8, 9, 10) (*----------------------------------------------------------*) sum(vec: array of integer): integer; sum(vec: array of real): real; product(vec: array of integer): integer; product(vec: array of real): real; Liefert die Summe bzw. das Produkt aller Komponenten von vec. (*----------------------------------------------------------------*) alloc(Arraytype, Len [,Ele]): Arraytype; Arraytype ist eines der Symbole array, string, byte_string. Die Funktion erzeugt ein Array (bzw. einen String, Byte-String) der Laenge Len, wobei alle Komponenten gleich Ele sind. Wird das Argument Ele nicht angegeben, so ist defaultmaessig Ele = 0, falls Arraytype = array, Ele = ' ', falls Arraytype = string und Ele = 0, falls Arraytype = byte_string. Beispiele: ==> alloc(array,10). -: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) ==> alloc(string,5,'A'). -: "AAAAA" ==> alloc(byte_string,5,127). -: $7F7F_7F7F_7F EINSCHRAENKUNG: Die Laengen von Arrays in ARIBAS koennen hoechstens 16-Bit-Zahlen sein. Meistens, insbesondere unter DOS, ist die Maximallaenge aus Speicherplatz-Gruenden viel kleiner. Es erscheint dann eine Fehlermeldung ==> alloc(array,20000). too large piece of memory requested ** RESET ** (*----------------------------------------------------------*) create_array(len: integer [; Ele: Type]): array of Type; Erzeugt ein Array der Laenge len, wobei alle Komponenten gleich Ele sind (Defaultwert fuer Ele ist 0). Aequivalent mit alloc(array,len,Ele). Nur zur Kompatibilitaet mit aelteren Versionen von ARIBAS, kuenftig wegfallend. (*----------------------------------------------------------*) sort(var vec: array of integer): array of integer; sort(var vec: array of real): array of real; sort(var vec: array of string): array of string; Das als variable-Parameter uebergebene Array von Zahlen bzw. Strings wird in aufsteigender Ordnung sortiert (bei Strings erfolgt die Ordnung nach den Ascii-Codes). Das sortierte Array wird zurueckgegeben. sort(var vec: array of Type; compfun: function): array of Type; Der Funktion sort kann als optionales zweites Argument eine Funktion uebergeben werden. Das erste Argument ist ein Array von Elementen eines beliebigen Datentyps Type. Die Funktion compfun muss eine Funktion mit zwei Argumenten des Typs Type sein und integer-Werte liefern. Es wird compfun(x,y) <= 0 als x <= y interpretiert. Beispiel: Sei etwa ein Array von Paaren ganzer Zahlen gegeben. Hierfuer definieren wir eine Vergleichsfunktion ==> function compare2(x,y: array[2]): integer; begin return y[1] - x[1]; end. -: compare2 ==> vec := ((1,7), (2,3), (3,4), (4,-1), (5,2)); sort(vec,compare2). -: ((4, -1), (5, 2), (2, 3), (3, 4), (1, 7)) (*----------------------------------------------------------*) g) Stacks ========= length stack_push stack_pop stack_top stack_reset stack_empty stack2array (*-----------------------------------------------------------------*) Es gibt keine Stack-Literale. Stacks werden initialisiert durch ein Variablen-Deklaration. Z.B. erzeugt die folgende Top-Level-Deklaration var st: stack; end. einen (leeren) Stack, auf den anschliessend mit stack_push Elemente gelegt werden koennen. (*-----------------------------------------------------------------*) length(st: stack): integer; Ergibt die Laenge des Stacks st, d.h. die Anzahl der Elemente (beliebigen Datentyps), die auf dem Stack abgelegt sind. (*-----------------------------------------------------------------*) stack_push(st: stack; ele: Type): Type; Legt ein Element ele des Datentyps Type (der beliebig sein kann) auf den Stack st. Rueckgabewert ist ele. Die Laenge des Stacks vergroessert sich um 1. (*-----------------------------------------------------------------*) stack_pop(st: stack): Type; Entfernt das oberste Element vom Stack st und gibt es zurueck. Es wird vorausgesetzt, dass der Stack nicht leer ist. Die Laenge des Stacks vermindert sich um 1. (*-----------------------------------------------------------------*) stack_top(st: stack): Type; Gibt das oberste Element des Stacks st zurueck; der Stack selbst bleibt aber unveraendert. (*-----------------------------------------------------------------*) stack_reset(st: stack): integer; Entfernt alle Elemente vom Stack st. Uebrig bleibt ein leerer Stack. Rueckgabewert 0. (*-----------------------------------------------------------------*) stack_empty(st: stack): boolean; Stellt fest, ob der Stack st leer ist. (*-----------------------------------------------------------------*) stack2array(st: stack): array of Type; Erzeugt ein Array der Laenge length(st) aus den Elementen, die auf dem Stack liegen. Das Element, das zuunterst auf dem Stack liegt, bekommt den Index 0. Uebrig bleibt ein leerer Stack. Der Programmierer ist selbst verantwortlich dafuer, dass alle Elemente des Stacks den richtigen Datentyp haben. (*-----------------------------------------------------------------*) h) In-Out ========= write writeln flush readln load open_read open_write open_append rewind close set_filepos get_filepos length read_byte read_block write_byte write_block Vordefinierte Files: stdin stdout stderr (*--------------------------------------------------------------------*) open_write(var f: file; name: string): boolean; Oeffnet eine Datei namens name zum Schreiben. Rueckgabewert: true bei erfolgreicher Oeffnung, sonst false. VORSICHT: Schon existierende Dateien werden ueberschrieben. (*--------------------------------------------------------------------*) open_append(var f: file; name: string): boolean; Oeffnet eine Datei namens name zum Schreiben. Falls die Datei schon vorhanden ist, bleibt ihr Inhalt erhalten; Schreib-Operationen erfolgen an das Ende der Datei. Ist die Datei noch nicht vorhanden, wird eine neue erzeugt. Rueckgabewert: true bei erfolgreicher Oeffnung, sonst false. (*--------------------------------------------------------------------*) open_read(var f: file; name: string): boolean; Oeffnet eine vorhandene Datei namens name zum sequentiellen Lesen mittels readln. Rueckgabewert: true bei erfolgreicher Oeffnung, sonst false. (*--------------------------------------------------------------------*) rewind(var f: file): boolean; Eine zum Lesen geoeffnete Datei, aus der schon einige Daten gelesen worden sind, kann mit rewind an den Anfang zurueckgesetzt werden. Rueckgabewert: true bei erfolgreicher Ausfuehrung. (*--------------------------------------------------------------------*) close(f: file): boolean; Schliesst das (vorher geoeffnete) File f. (*--------------------------------------------------------------------*) length(f: file): integer; Das File f muss zum Lesen geoeffnet sein. Dann liefert die Funktion length die Laenge der Datei in Bytes. (*--------------------------------------------------------------------*) Lese- und Schreib-Operationen fuer Text-Dateien readln, write, writeln readln([f: file;] var arg1,...,argn): integer; Liest aus der zum Lesen geoeffneten Datei f (falls dies Argument nicht angegeben wird, wird stdin angenommen, also von der Console gelesen) eine Zeile. Die Argumente muessen Variable vom Typ integer, real, char oder string sein. Rueckgabewert ist die Anzahl der erfolgreich gelesenen Argumente. Ist vor der Ausfuehrung von readln bereits das Datei-Ende erreicht, wird -1 zurueckgegeben. (*--------------------------------------------------------------------*) write([f: file;] arg1,...,argn): integer; writeln([f: file;] arg1,...,argn): integer; Schreibt die Argumente arg1,...,argn in eine zum Schreiben geoeffnete Text-Datei f. Die Argumente argi koennen von beliebigem Datentyp sein. Die Funktion writeln schliesst die Schreib-Operationen mit einem Zeilen-Vorschub ab. Wird das file-Argument f nicht angegeben, so wird default-maessig stdout (Terminal) angenommen. Rueckgabewert ist die Anzahl der geschriebenen Argumente. FORMAT-OPTIONEN fuer die Funktionen write und writeln. Wie in Pascal koennen Argumente bestimmter Datentypen noch mit Formatangaben versehen werden. Die Formatangaben, die im folgenden naeher beschrieben werden, sind vom jeweiligen Argument durch einen Doppelpunkt getrennt. a) String, Character Ist str ein String- oder Character-Ausdruck und width ein Integer-Ausdruck, so wird durch ein Argument der Form str: width die Breite der Ausgabe bestimmt. Ist der Wert von width eine ganze Zahl, die groesser als die Laenge von str ist, so wird durch Voranstellen von Leerzeichen die Ausgabe von str rechtsbuendig auf die Gesamtbreite width gebracht. Ist der Wert von width negativ und abs(width) groesser als die Laenge von str, so erfolgt die Ausgabe linksbuendig durch das Nachstellen von Leerzeichen mit einer Gesamtbreite von abs(width). Ist abs(width) kleinergleich der Laenge von str, so wird die Formatangabe ignoriert, ebenso wenn abs(width) groesser als die Zeilenlaenge ist. Beispiel: ==> writeln("abc":10,'X':10,"###"); writeln("def":-10,'Y':-10,"###"). abc X### def Y ### -: 3 b) Integer Ein Integer-Argument mit Formatangabe der Gestalt x: width wobei x und width Integer-Ausdruecke sind, bestimmt analog zum Fall der Strings rechts- bzw. linksbuendige Ausgabe des Integers x. Beispiel: ==> writeln(3**30:20,'|'); writeln(3**30:-20,'|'). 20589_11320_94649| 20589_11320_94649 | -: 2 In Erweiterung der Moeglichkeiten gegenueber Pascal sind bei Integern jedoch noch andere Formatangaben moeglich. Diese werden ebenfalls durch einen Doppelpunkt getrennt und haben die Gestalt base(n) group(n) digits(n) mit einem Integer-Ausdruck n. Die Formatangabe base(n), wobei n einen der Werte 2,8,10,16 annehmen darf, bestimmt die Basis der Ausgabe. Beispiel: ==> x := 3**9; writeln(x:10, x:20:base(2), x:10:base(8), x:10:base(16)). 19683 1001100_11100011 46343 4CE3 -: 4 ARIBAS gliedert die Ausgabe von grossen ganzen Zahlen durch den Unterstrich. Dies kann durch die Formatangabe group(n) gesteuert werden. Dabei kann der Wert von n entweder 0 oder eine ganze Zahl groessergleich 2 und kleiner als die Zeilenlaenge sein. Bei der Formatangabe group(0) erfolgt keine Gliederung; sonst werden jeweils n Ziffern zu einer Gruppe zusammengefasst, die durch einen Unterstrich von den anderen Gruppen getrennt werden. Beispiel: ==> x := 3**100; writeln(x); writeln(x: group(0)); writeln(x: group(10)). 515_37752_07320_11331_03646_11297_65621_27270_21075_22001 515377520732011331036461129765621272702107522001 51537752_0732011331_0364611297_6562127270_2107522001 -: 1 (Defaultmaessig verwendet ARIBAS bei Zahlen >= 2**32 im Dezimalsystem eine Gliederung zu je 5 Ziffern.) Durch die Formatangabe digits(n) kann die Ausgabe fuehrender Nullen erzwungen werden. Ist n groesser als die Stellenzahl einer ganzen Zahl x in einer bestimmten Basis, so werden entsprechend viele fuehrende Nullen ergaenzt, so dass die Gesamtzahl der ausgegebenen Ziffern gleich n ist. Ist n kleinergleich der Stellenzahl, so hat die Formatangabe digits(n) keine Wirkung. Beispiel: ==> for k := 3 to 10 do writeln(k: 10: base(2): digits(4): group(2)); end. 00_11 01_00 01_01 01_10 01_11 10_00 10_01 10_10 -: Die Formatanweisungen base, digits, group duerfen in beliebiger Reihenfolge stehen. c) Real ==> writeln(pi:15). 3.141593E+0000 -: 1 ==> writeln(pi:15:10). 3.1415926536 -: 1 ==> writeln(pi:12:10). 3.1415926536 -: 1 (*--------------------------------------------------------------------*) flush([f: file]); Bewirkt nach einer write-Operation in das File f (default = stdout), dass die zu schreibenden Daten, die sich moeglicherweise noch in einem Puffer befinden, sofort hinausgeschrieben werden. (*--------------------------------------------------------------------*) load(fnam: string): boolean; fnam muss der Name einer Text-Datei mit ARIBAS-Source-Code sein. Die Extension .ari kann weggelassen werden. Durch load wird diese Datei eingelesen und es werden die darin befindlichen Befehle und Funktions-Definitionen ausgefuehrt, so als ob sie direkt eingegeben worden waeren. Rueckgabewert bei erfolgreichem Laden ist true. (*--------------------------------------------------------------------*) BINAERDATEIEN: Der Default-Typ einer Datei in ARIBAS ist die Text-Datei. Es koennen jedoch auch Binaerdateien zum Schreiben oder zum Lesen geoeffnet werden. Dazu ist in den Funktionen open_write, open_read, open_append als drittes Argument das Symbol binary anzugeben; Beispiel: ==> open_read(f,"BIN.DAT",binary). Dies oeffnet die Datei mit dem Namen "BIN.DAT", die als existent vorausgesetzt wird, zum Lesen. Fuer Binaerdateien gibt es die Schreib-Operationen write_byte und write_block sowie die Lese-Operationen read_byte und read_block. Die Funktionen rewind und length koennen auch auf Binaerdateien, die zum Lesen geoeffnet sind, angewendet werden. (*--------------------------------------------------------------------*) set_filepos(f: file; pos: integer): integer; f muss ein zum Lesen geoeffnetes binaeres File und pos eine Zahl mit 0 <= pos < length(f) sein. Dann setzt set_filepos die Position fuer die naechste Lese-Operation auf pos. Rueckgabewert ist dann ebenfalls pos. Liegt pos nicht im zulaessigen Bereich, so erfolgt keine Aktion und die aktuelle File-Position wird zurueckgegeben. (*--------------------------------------------------------------------*) get_filepos(f: file): integer; f muss ein zum Lesen geoeffnetes binaeres File sein. Es wird die aktuelle File-Position zurueckgegeben. (*--------------------------------------------------------------------*) read_byte(f: file): integer; Liest aus einer zum Lesen geoeffneten Binaer-Datei f das naechste Byte, das als integer 0 <= b <= 255 zurueckgegeben wird und erhoeht die File-Position um eins. Falls aber beim Aufruf von read_byte die File-Position bereits das Datei-Ende ist, wird -1 zurueckgegeben und die File-Position nicht veraendert. (*--------------------------------------------------------------------*) read_block(f: file; var block: byte_string; len: integer): integer; f muss eine zum Lesen geoeffnete Binaer-Datei sein und block eine byte_string-Variable mit einer aktuellen Laenge >= len oder ein Subarray eines byte_strings mit einer Laenge >= len sein. Dann liest read_block aus dem File f die naechsten len Bytes und schreibt sie in die ersten len Komponenten von block. Wenn vorzeitig das Datei-Ende erreicht wird, wird die Lese-Operation abgebrochen. Der RueckgabeWert der Funktion ist die Anzahl der erfolgreich gelesenen Bytes, im Normalfall also gleich len. (*--------------------------------------------------------------------*) write_byte(f: file; x: integer): integer: Schreibt in eine (mittels open_write oder open_append) zum Schreiben geoeffnete Binaer-Datei f das durch einen integer-Wert x mit 0 <= x <= 255 gegebene Byte. Statt eines Integers kann x auch ein Character sein. Rueckgabewert ist bei erfolgreicher Schreibaktion das geschriebene Byte, im Fehlerfall -1. (*--------------------------------------------------------------------*) write_block(f: file; var block: byte_string; len: integer): integer; f muss eine zum Schreiben geoeffnete Binaer-Datei sein und block eine byte_string-Variable mit einer aktuellen Laenge >= len oder ein Subarray eines byte_strings mit einer Laenge >= len sein. write_block schreibt die ersten len Bytes von block in das File f. Der Rueckgabe-Wert der Funktion ist die Anzahl der erfolgreich geschriebenen Bytes, bei fehlerfreier Ausfuehrung also gleich len. (*-----------------------------------------------------------------*) i) System-Funktionen ==================== memavail gc timer exit halt make_unbound symbols help system protocol save_input load_edit (*-----------------------------------------------------------------*) halt([retcode: integer]): integer; Wird die Funktion halt aufgerufen, so wird die laufende ProgrammAusfuehrung gestoppt und sofort zum Top-Level zurueckgekehrt (auch wenn der halt-Aufruf innerhalb eines geschachtelten FunktionsAufrufs erfolgte). Als Ergebnis wird das optionale Argument retcode (ein 16-Bit integer) geliefert, defaultmaessig 0. Die Funktion halt ist vorallem zum Abfangen schwerer Fehler geeignet. (*-----------------------------------------------------------------*) memavail(): integer; Ergibt den derzeit freien Speicherplatz auf dem ARIBAS-heap in Kilobytes und schreibt zusaetzliche Informationen. Beispiel: ==> memavail(). total number of garbage collections: 1 96000 Bytes reserved; 96000 Bytes active (54780 used, 41220 free) 11417 Bytes free for user defined symbols and symbol names -: 40 Da ARIBAS einen garbage collector besitzt, der nach dem HalbraumVerfahren arbeitet, ist der ARIBAS-heap ist in zwei gleich grosse Teile aufgeteilt (in diesem Beispiel zu je 96000 Bytes). Davon ist ein Teil aktiv, aus ihm werden Speicherplatz-Anforderungen fuer ARIBAS-Objekte (z.B. grosse Integers) erfuellt. Im obigen Beispiel sind noch 41220 Bytes davon frei, das sind gerundet 40 KB. (Die andere Haelfte liegt nicht brach, sondern wird z.B. als Zwischenspeicher fuer Rechnungen genutzt.) Ist der Speicherplatz des aktiven Halbraums erschoepft, so erfolgt automatisch eine garbage collection. Die Gesamtzahl der seit Beginn der laufenden ARIBAS-Sitzung durchgefuehrten garbage collections wird ebenfalls gemeldet. Namen von benutzer-definierten Variablen und Funktionen werden von ARIBAS in einer Symboltabelle eingetragen. Der hier noch freie Platz wird ebenfalls gemeldet. Man kann alle Meldungen ausschalten, indem man memavail mit dem zusaetzlichen Argument 0 aufruft. ==> memavail(0). -: 40 (*-----------------------------------------------------------------*) gc(): integer; Erzwingt eine garbage collection und liefert als Ergebnis den danach freien Speicherplatz fuer ARIBAS-Objekte in Kilobytes. Es werden dieselben Meldungen wie bei memavail ausgegeben. Eine stumme Version ist gc(0). Diese ist z.B. nuetzlich, wenn man die Ausfuehrung einer bestimmten Prozedur davon abhaengig machen will, dass ein gewisses Mindestmass von Speicher frei ist, etwa if gc(0) < 64 then writeln("not enough memory for procedure foo"); else foo(...); ... end; (*-----------------------------------------------------------------*) timer(): integer; Liefert die Zeit in Millisekunden, die seit einem von der jeweiligen Computer-Sitzung abhaengigen Anfangspunkt vergangen ist. (Die Genauigkeit der Zeitmessung ist systemabhaengig.) Damit kann man z.B. die Zeit stoppen, die fuer die Ausfuehrung gewisser Funktionen gebraucht wird. ==> t := timer(); x := isqrt(2*10**2000); timer() - t. -: 216 Im obigen Beispiel, das mit einem 80486-Prozessor mit 33 MHz Taktfrequenz durchgefuehrt wurde, wurde die Quadratwurzel aus 2 mit 1000 DezimalStellen Genauigkeit in 216 Millisekunden berechnet. (*-----------------------------------------------------------------*) symbols(aribas) Ergibt eine Liste der ARIBAS-Schluesselworte und eingebauten Funktionen. Das Symbol aribas muss so wie es steht eingegeben werden. symbols(user) Ergibt eine Liste der derzeit benutzer-definierten Variablen und Funktionen. (*-----------------------------------------------------------------*) help(Topic) Gibt eine kurze Online-Hilfe ueber Topic. Als Topic kommen die meisten Symbole in Frage, die man mit dem Befehl symbols(aribas) erhaelt, z.B. liefert ==> help(factor16). eine kurze Beschreibung der eingebauten Funktion factor16. Damit das help-Kommando funktioniert, muss sich die Datei aribas.hlp mit den Hilfe-Texten im selben Directory wie aribas.exe befinden (dies gilt fuer die DOS-Version). (*-----------------------------------------------------------------*) system(command: string): integer; Der String command wird an den Kommando-Interpreter des Systems zur Ausfuehrung uebergeben. Rueckgabewert ist meistens 0, oder ein Fehler-Code. Beispielsweise erzeugt ==> system("dir"). unter DOS die Ausgabe des Inhalts-Verzeichnis des aktuellen Directory's (*-----------------------------------------------------------------*) 7) FUNKTIONS-DEFINITIONEN ========================= function procedure external const var begin end return (*--------------------------------------------------------------------*) Funktionen werden in ARIBAS (wie in C) alle auf gleicher Ebene definiert; geschachtelte Funktions-Definitionen sind nicht zulaessig. In Funktions-Definitionen darf auf andere Funktionen Bezug genommen werden, auch wenn diese noch nicht definiert sind. Es sind keine FORWARD-Deklarationen wie in Pascal noetig. Es liegt in der Verantwortung des Programmierers, dafuer zu sorgen, dass alle benoetigen Funktionen definiert sind, wenn der tatsaechliche Funktions-Aufruf erfolgt. (*--------------------------------------------------------------------*) Eine Funktions-Definition hat folgende Gestalt function Funame(<formale Parameterliste>): Resulttype; <external Deklaration> <const Deklaration> <var Deklaration> begin <statemement list> end. Statt function darf man auch procedure schreiben (zur Kompatibilitaet mit Modula-2). Dabei muss Funame ein zulaessiger Identifier sein, der nicht mit einem Schluesselwort oder dem Namen einer eingebauten Funktion zusammenfaellt. Die formale Parameterliste kann wie in Pascal oder Modula-2 Wert- und Variablen-Parameter enthalten. Sie kann auch leer sein (man darf aber das Klammerpaar nicht weglassen). Die external-, const- und var-Deklarationen, die wir spaeter besprechen, koennen auch fehlen. Im Hauptteil der Funktions-Definition zwischen begin und end koennen Return-Statements der Form return Retval; vorkommen, wobei Retval den Datentyp Resulttype haben muss. In ARIBAS koennen auch zusammengesetzte Typen wie Arrays als Funktions-Ergebnisse verwendet werden. Beispiele: (*--------------------------------------------*) function mersenne(n: integer): integer; begin return 2**n - 1; end. (*--------------------------------------------*) Diese Funktion berechnet die n-te Mersenne-Zahl ==> mersenne(59). -: 576_46075_23034_23487 Bemerkung: Der Punkt am Ende der Funktions-Definition ist nur noetig, wenn man die Funktion interaktiv am ARIBAS-Prompt eingibt. Falls die Funktions-Definition in einer Datei steht, die mittels load von ARIBAS geladen wird, kann man auch ein Semicolon setzen. Das folgende ist eine rekursive Funktion zur Berechnung der Fakultaet (*---------------------------------------------------------*) function fac_rec(n: integer): integer; begin if n <= 2 then return n; else return fac(n-1)*n; end end. (*---------------------------------------------------------*) Falls die Funktion lokale Variable benoetigt, muessen diese deklariert werden, wie im folgenden Beispiel, das eine iterative Version des vorhergehenden Beispiels darstellt. function fac_it(n: integer): integer; var i,x: integer; begin x := 1; for i := 2 to n do x := x*i; end; return x; end. (*---------------------------------------------------------*) Im Gegensatz zu Pascal braucht bei der Variablen-Deklaration von Arrays die Laenge keine Konstante zu sein. Beispiel: (*---------------------------------------------------------*) function squarelist(n: integer): array; var k: integer; vec: array[n]; begin for k := 1 to n do vec[k-1] := k*k; end; return vec; end. (*---------------------------------------------------------*) Diese Funktion stellt ein Array der Laenge n mit den Quadratzahlen von 1 bis n**2 her. ==> squarelist(5). -: (1, 4, 9, 16, 25) (*---------------------------------------------------------*) Auf benutzer-definierte globale Variable kann innerhalb von Funktionen nur zugegriffen werden, wenn sie explizit als external deklariert werden. Dies ist eine Vorsichts-Massnahme, da globale Variable einfach durch Zuweisungen entstehen koennen. (Das Gleiche gilt fuer globale benutzer-definierte Konstanten.) Beispiel: (*---------------------------------------------------------*) function count(): integer; external Counter: integer; begin return inc(Counter); end; (*---------------------------------------------------------*) Dies ist gleichzeitig ein Beispiel fuer eine Funktion ohne Argumente. Es wird vorausgesetzt, dass die Integer-Variable Counter existiert, wenn count aufgerufen wird. ==> Counter := 7; count(). -: 8 Gleichzeitig ist die Variable Counter um 1 erhoeht worden. ==> Counter. -: 8 Dieselbe Wirkung kann man erzielen, wenn man Counter als Variablen-Argument uebergibt. (*---------------------------------------------------------*) function count1(var Counter: integer): integer; begin return inc(Counter); end; (*------------------------------------------------------------*) Falls Konstanten-Deklarationen vorkommen, stehen sie nach den External-Deklarationen und vor den Variablen-Deklaration. Sie haben eine Syntax aehnlich wie in Pascal und Modula-2. In ARIBAS koennen jedoch z.B. auch Array-Konstanten definiert werden. Beispiel: (*------------------------------------------------------------*) function dayofweek(n: integer): string; const Week = ("SU", "MO", "TU", "WE", "TH", "FR", "SA"); begin return Week[n mod 7]; end; (*------------------------------------------------------------*) ==> dayofweek(4). -: "TH" (*------------------------------------------------------------*) Optionale Argumente von Funktionen ---------------------------------Man kann in ARIBAS auch Funktionen mit optionalen Argumenten definieren. Dazu muessen am Ende der formalen Parameterliste statt der Typ-Vereinbarungen von Wert-Parametern Zuweisungen der Form <identifier> := Value stehen. Wird beim Funktions-Aufruf das Argument weggelassen, verwendet die Funktion den Wert Value. Wird das Argument beim FunktionsAufruf angegeben, darf es einen beliebigen Wert vom gleichen Datentyp wie Value haben. Beispiel (*------------------------------------------------------------*) function ranvec(len: integer; bound := 1000): array; var vec: array[len]; i: integer; begin for i := 0 to len-1 do vec[i] := random(bound); end; return vec; end; (*------------------------------------------------------------*) Diese Funktion liefert ein Array der Laenge len aus Zufallszahlen. Wird die Funktion nur mit dem Argument len aufgerufen, wreden die Zufallszahlen dem Intervall 0 <= x < 1000 entnommen; mit zwei Argumenten len und bound stammen die Zufallszahlen aus dem Intervall 0 <= x < bound. ==> ranvec(12). -: (923, 23, 510, 475, 970, 974, 5, 553, 175, 700, 891, 411) ==> ranvec(12,100). -: (15, 95, 55, 99, 17, 63, 7, 82, 24, 62, 49, 10) (*------------------------------------------------------------*) 8) VARIABLEN-DEKLARATIONEN ========================== Es gibt globale Variablen-Deklarationen und lokale VariablenDeklarationen (d.h. innerhalb von Funktions-Definitionen). Eine globale Variablen-Deklaration (auf dem Top-Level) hat folgende Gestalt: var <identifier_list1>: <type1>; ... <identifier_listn>: <typen>; end Eine Variablen-Deklaration innerhalb einer Funktions-Definition sieht aehnlich aus, nur steht statt des Symbols end das Symbol begin, das den Anfang des Funktionskoerpers bezeichnet. Dabei ist <identifier_listk> eine durch Kommata getrennte Folge von Variablen-Namen. <typek> ist eine der Typ-Bezeichnungen integer, real, char, boolean, stack, file oder ein String- oder Array-Typ, zu denen spaeter noch mehr zu sagen ist. Beispiel: ==> var n,m: integer; x: real; c1,c2: char; st: stack; end. -: var Nach dieser Variablen-Deklaration existieren Integer-Variablen n und m, eine Real-Variable x, zwei Character-Variable c1 und c2, sowie eine Stack-Variable st. Durch eine Variablen-Deklaration werden die Variablen durch ARIBAS auch initialisiert, und zwar Integers durch 0, Reals durch 0.0, boolesche Variablen durch false, Characters durch ' ' (d.h. das Space-Zeichen) und Stack-Variable durch den leeren Stack. Arrays und Strings koennen mit und ohne Laengenangabe deklariert werden. Die Deklaration mit Laengenangabe geht wie in folgendem Beispiel: ==> var str: string[4]; vec: array[10] of real; end. -: var Hiermit wird ein String der Laenge 4, der mit Leerzeichen initialisiert ist, sowie ein real-Array der Laenge 10, initialisiert mit den Elementen 0.0, erzeugt. Man beachte jedoch, dass in ARIBAS Strings und Arrays verschiedener Laenge zuweisungs-kompatibel sind, somit anschliessend etwa eine Zuweisung ==> str := concat(str,"___"). -: " ___" zulaessig ist. Wird die Laengenangabe weggelassen, so werden Strings oder Arrays der Laenge 0 erzeugt: ==> var str: string; vec: array; end. -: var ==> str. -: "" ==> vec. -: () Jedoch koennen auch hier den Variablen str bzw. vec anschliessend Strings bzw. Arrays positiver Laenge zugewiesen werden. Die Laengenangaben von Arrays (sowie Strings und Byte-Strings) brauchen keine Konstanten zu sein, sondern koennen auch integer-Ausdruecke sein, wie in folgendem Beispiel: ==> n := 5. -: 5 ==> var vec: array[2*n+1]; end. -: var ==> vec. -: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) (Wird der Datentyp der Elemente eines Arrays nicht spezifiziert, so wird der Default-Datentyp integer angenommen.) Kontanten-Deklarationen ======================= Konstanten-Deklarationen gibt es (wie Variablen-Deklarationen) sowohl innerhalb von Funktions-Definitionen als auch auf globaler Ebene. Eine Top-Level-Konstanten-Deklaration hat die Gestalt const Identifier1 = Value_1; .... Identifiern = Value_n; end. (Innerhalb von Funktionen entfaellt end; die Konstanten-Definition endet mit dem Beginn der Variablen-Deklaration (var) oder, falls eine solche fehlt, mit dem Funktions-Koerper (begin).) Die Werte Value_k duerfen sowohl Literale, als auch arithmetische Ausdruecke oder Funktions-Ausdruecke mit vorher definierten oder eingebauten Funktionen sein. Beispiel: ==> const Bound1 = 2**16 - 1; Bound2 = round(exp(10)); end. -: const (*----------------------------------------------------------*) 9) KOMANDOZEILEN-ARGUMENTE ========================== Beim Aufruf von ARIBAS koennen in der Kommandozeile Optionen und der Name einer .ari-Datei als Argumente uebergeben werden: Moegliche Optionen: -q (quiet) Unterdrueckt die Meldungen beim Start von ARIBAS -cXX Hierbei steht XX fuer eine Zahl (in Dezimaldarstellung) zwischen 40 und 160. Dadurch wird festgelegt, wieviele Spalten in der BildschirmAusgabe benutzt werden. Defaultwert ist 80. -mXXX Hierbei steht XXX fuer eine Zahl (in Dezimaldarstellung) zwischen 64 und 4096. Sie gibt an, wieviel tausend Bytes ARIBAS maximal fuer den ARIBAS-heap bereitstellen soll. (Siehe dazu die Funktion memavail.) Als weiteres Kommandozeilen-Argument kann der Name einer Datei mit ARIBAS-Quellcode angegeben werden, die dann geladen wird. Die Extension .ari kann weggelassen werden. Beispiel: aribas -c72 -m200 factor Dieser Aufruf von ARIBAS stellt die Ausgabeweite auf 72 Spalten ein, reserviert 200000 Bytes fuer den ARIBAS-heap in zwei Halbraeumen zu 100000 Bytes (falls moeglich) und laedt die Datei factor.ari aus dem aktuellen Directory (falls dort vorhanden). Will man mehrere Dateien beim Programmstart laden, kann man z.B. eine Datei startup.ari anlegen, in der mehrere load-Anweisungen stehen und dann aribas startup aufrufen. Folgen dem Namen der zu ladenden Datei noch weitere Argumente, stehen diese als Strings innerhalb von ARIBAS zur Verfuegung. In ARIBAS gibt es eine globale Variable ARGV, die ein Array von Strings ist. Die nullte Komponente ist der Name der geladenen Datei, die weiteren Komponenten sind die zusaetzlichen Kommandozeilen-Argumente. Beispiel: Nach dem Aufruf aribas -q startup 4536 eisenstein hat der Vektor ARGV die Form ==> ARGV. -: ("startup", "4536", "eisenstein") Braucht man einige der Argumente als Zahlen und nicht als Strings, kann man die entsprechenden Komponenten von ARGV durch atoi (oder atof) umwandeln; in unserem Beispiel etwa ==> atoi(ARGV[1]). -: 4536 Alle Kommandozeilen-Argumente sind optional. (*************************** EOF ******************************)