Szintaxis
Alprogramból itt is kétféle van, az eljárás és a függvény. A kettő között lényeges különbség, hogy az eljárásnak nem lehet visszatérő értéke.
Általában azt szokták mondani, hogy függvényt akkor használunk amikor valamit ki akarunk számolni, eljárást pedig minden másra. Az osztályoknál még előjön majd a metódus is.
Ha egy függvény vagy eljárás statikus, akkor csak az adott modulban látszik. Mindkettőnél megadható a hivási konvenció, például pascal.
static function add(a, b) as int pascal
return a + b
function Start(p)
? add(3, 4) // vagy do add with 3, 4
_Accept("")
return nil
A lehetséges hívási konvenciók:
- clipper
- strict
- pascal
- callback
Az eljárás teljesen hasonló, viszont mindig
nil -t ad vissza, ebbe mi nem szólhatunk bele. Egy érdekes különbség, hogy megadhatunk úgynevezett INIT eljárásokat.
Ezek az alkalmazás indításakor automatikusan meghívódnak, még a
Start() rutin előtt. Három prioritási szint van, ezek a hívások sorrendjét befolyásolják, vagyis először az
_INIT1 eljárások hívódnak meg,
aztán az
_INIT2 -esek, és utána az
_INIT3 -asok. A reference guide hangsúlyozza, hogy a hibakezelő rutinok
_INIT1 -esek, ezért olyat mi lehetőleg ne használjunk.
Ezenkívül az INIT eljárásokra van néhány megszorítás is:
- nem lehetnek argumentumai
- strict vagy pascal hívási konvencióval kell deklarálni
procedure test as void pascal _INIT2
? "elso, haha"
function Start(p)
? "nahanyadiklesz"
// output: elso, haha nahanyadiklesz
Még egy baromi érdekes dolog, ami függvényeknél és eljárásoknál is működik, az
export local utasítás. Itt előjönnek a kódblokkok is, amikről
nem írtam még, de most megnézzük milyen állatok is azok. Képzeljük el a kódblokkot úgy, mint egy programrészletet amit akár paraméterként is átadhatunk egy függvénynek.
Nézzünk egy példát.
function Start(p)
local cb as codeblock
cb := {|x| x + 2}
? Eval(cb, 10)
_Accept("")
return nil
// output: 12
Nagyon hasonlít az új C++ szabvány lambda kifejezéseire.
Most képzeljük el, hogy írtunk egy kódblokkot, ami mondjuk az
x() függvény egy lokális változóját használja, majd a kódblokkot
átadjuk paraméterként az
y() függvénynek. Mi a fene lesz azzal a változóval? Itt jön be a képbe az
export local, ennek segítségével
a kódblokkban láthatóvá válnak a hívó függvény lokális változói.
function x as void export local
local i := 3 as int
local cb as codeblock
cb := { |x| x + i }
? y(cb)
function y(cb as codeblock) as int
return Eval(cb, 5)
// output: 8
A vicc az, hogy nálam
export local nélkül is megy. Hehe.
Paraméterátadás
Eddig csak érték szerinti paraméterátadást láttunk, de persze létezik cím szerinti is. Ekkor az as kulcsszó helyett a ref -et kell
használni, és a függvény hívásakor az adott paramétert a @ operátorral kell átadni.
function x(a ref int) as void
a := 2
function Start(p)
local i as int
x(@i)
? i
_Accept("")
return nil
// output: 2
Túlterhelés
Egy modulon belül nem lehet két azonos nevű elem, így függvényeket nem tudunk túlterhelni. A rosszabb hír, hogy ha egy elem az egész alkalmazásban látszik,
akkor aztán tényleg sehol nem lehet ugyanolyan nevű elem.
Rekurzió
Természetesen egy függvény hívhatja önmagát is.
function Factorial(n as int) as int
if n = 0 .or. n = 1
return 1
endif
return Factorial(n - 1) * n
Mily meglepő, eljárásokra is működik. A
Factorial2 eljárást lehetne szebben is csinálni, például külön átadni az értéket, de most lusta voltam.
procedure Factorial2(n ref int)
local tmp as int
if n = 0 .or. n = 1
n := 1
return
endif
tmp := n - 1
Factorial2(@tmp)
n := n * tmp
function Start(p)
local i := 5 as int
? Factorial(3)
Factorial2(@i)
? i
_Accept("")
return nil
// output: 6 120
Megpróbáltam kódblokkal is, de nem enged elágazást (önmagát hívni még igen, de stack overflowot kaptam). Sajnos erről sem találni túl sokat a
helpben.