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).