Az Euclid programozási nyelv

Alprogramok

Rutinok az EUCLID-ban

Egy rutintörzsben egy nem lokális azonosító csak akkor látható, hogyha látható a közvetlenül felette lévő befoglaló láthatósági körben, vagy explicit importáltuk az adott rutintörzsbe(import,pervasive).A rutinhívás tartalmazza az aktuális paramétereket, melyeket sorrendben feleltet meg a formális paramétereknek.Megadható még egy inline prefix is, ami a fordítónak szóló javaslat, hogy a rutin minden hívásához másolja át a rutin törzsét, ezzel gyorsítva a program futását. Ez nyilván csak rövid rutinok esetében kifizetődő.
Kétféle paraméter van: konstans és változó; rutin és típus paraméterek nincsenek megengedve. Ha a paraméter konstans, akkor a megfelelő formális paraméter egy lokális konstanst reprezentál, és az aktuális paraméternek kifejezésnek kell lennie. Változó paraméter esetén az aktuális paraméternek változónak kell lennie. Függvényeket az eljárásokkal analóg módon kell deklarálni, kettőjüket közösen rutinoknak nevezzük. A függvényeknek csak konstans paraméterei lehetnek, nem importálhatnak változót (bár readonly paraméter engedélyezett). Ezzel a "side effect" kizárása garantált - joggal szerepel tehát a nyelv paradigmái közt a funkcionális is.

Függvények

A függvények az EUCLID-ban arra szolgálnak, hogy valamilyen bemenő adatokból valamilyen értéket számítsanak ki. A bemenő adatokat tehát egy függvény nem változtathatja meg. A függvény neve után következnek a formális paraméterek, majd a returns után a visszaadandó érték és az ő típusa. = jel után pedig a program, ami az értéket kiszámítja. Aktuális paraméter lehet egy újabb függvény is, ekkor mindig a legbelső függvény értékelődik ki, mint minden más normális strukturált programnyelvben. A következő függvény megadja egy tömb maximális elemének indexét adott indexhatárok között.

function FindMax ( a :DataArraySegment(parameter, parameter) ) returns index : signedInt = post { k in a.m .. a.n -> a(index)>=a(k) } begin index := a.m for i in a.m+1 .. a.n loop assert { k in a.m .. i-1 -> a(index)>= a(k) } if a(i) > a(index) then index := i end if end loop end FindMax


(A post és assert szavakat nem kötelező kitennünk, az utánuk következő {} jelek között megjegyzésként leírhatunk invariáns tulajdonságokat, vagy az utófeltételt. lásd még Helyességbizonyítás nyelvi eszközei)
A függvények hívása v := f(..) alakú.

Eljárások

Az eljárást a nevével és az aktuális paraméterekkel hívjuk meg önmagában. Az eljárás paraméterei lehetnek konstansok, vagy változók. A paraméterek prefixe lehet const, var, vagy readonly. Ha nincs megadva prefix, akkor konstansnak tekintendő a paraméter. A formális paraméterek típusspecifikációja tartalmazhat aktuális paramétereket, amik szintén formális paraméterek, így a

procedure f(n: 0..1000, a: array 1..n of signedInt) ...


deklaráció legális. Egy eljárás rekurzív hívása szintén megengedett,függvényé is. Egy eljárásban megadhatók újabb eljárások és utasítások. A kilépő utasítások az exit,illetve a return,melyeket egy when kifejezéssel egészíthetünk ki. A return hatására egy rutinból térünk vissza; az exit-nél egy ciklusból lépünk ki.

Egy tömböt rendező eljárás :

type DataArray ( n :1..256 ) = array 1..n of signedInt; { ez az adattomb tipus, az n adja meg a tomb hosszat, a tomb elemei signedInt tipusuak } procedure TreeSort ( var a :DataArray(parameter)) = post { (a in Perm(a') and j in 1..a.n-1) -> a(j) <=a(j+1) } { ez tehat az utofeltetel } begin type Index = a.IndexType { IndexType mezoje minden tombnek van } inline procedure Swap ( i1, i2 :Index ) = { beepitett fugveny, kivulrol nem lathato } imports (var a) { ezeket hasznalja es megvaltoztathatja } post { a in Perm(a') and a(i1)=a'(i2) and a(i2)=a'(i1) } { azaz megcsereli az a ket elemet } begin const t := a(i1) a(i1) := a(i2); a(i2) := t end Swap; procedure ShiftUp ( low, high :Index) = imports (var a) { a rendezofaban mozgatja az elemeket } . . . end ShiftUp; for i decreasing in 1..(Index.last div 2) loop ShiftUp (i, Index.last) end loop for i decreasing in 1..Index.last-1 loop Swap(1, i+1); ShitUp(1, i) end loop end TreeSort { itt van az eljaras vege }


A rekurzív függvényre példa:

function Gcd(m, n:. signedInt) returns signedInt = imports(Gcd) begin if n=0 then return m e1se return Gcd(n, m mod n) end if end Gcd

Euclid rutinok hívása gépi kódból

Motiváció : önfordító compiler esetén, amely cserélhető kódgenerátorokat használ egy-egy számítógép-architektúrához, igencsak hasznos lehet.
Hogyan : Adott az R Euclid-rutin. Vegyük a következő deklarációt:

var Rlink : routineLink (at 100) := routineLink <<=procedure(R)

Rlink változónk olyan "rutinra való hivatkozás", melynek a kezdőcíme a 100. tárolási egységben található (lsd. Gépfüggő modulok). Az R(amit értékül kap) egy routineLink -é konvertált rutin(eljárás v. függvény), amelyiknek kezdőcíme így a 100. tárolási egységbe kerül. Ezzel még nem vagyunk készen: a gépi kódba még be kell írnunk egy megfelelő ugró utasítást a 100. tárolási egységben lévő címre. A routineLink típust persze a programban korrektül definiálni kell (mondjuk gépfüggő modulként).