first, previous, next, last section, table of contents.


Felhasználó által definiált függvények

Bonyolult awk programokat egyszerűsíteni lehet, ha saját függvényeket definiálsz. Az általad definiált függvényeket ugyanúgy kell meghívni, mint a beépített függvényeket (see section Függvényhívások), de neked kell definiálni a függvényeket -- megmondani az awk-nak, hogy a függvénynek mit kell csinálnia.

A függvénydefiníció formája

A függvények definíciója bárhol előfordulhat az awk program szabályai között. Így egy awk program általános formája valójában szabályok és függvénydefiníciók sorozata. Nincs szükség arra, hogy a függvények definícióját a használatuk előtt adjuk meg, mivel az awk először beolvassa az egész programot, és csak utána kezdi el végrehajtani.

Egy name nevű függvény definíciója így néz ki:

function name(parameter-list)
{
     body-of-function
}

Egy érvényes függvény neve ugyanolyan, mint egy érvényes változó neve: betűk, számok és az aláhúzás karakterek sorozata, amelyek nem kezdődhetnek számmal. Egy awk programon belül egy adott név vagy változó, vagy tömb, vagy függvény.

A parameter-list a függvény argumentumainak és a lokális változóknak a listája, vesszővel elválasztva az elemeit. Amikor egy függvényt meghívunk, akkor az argumentumban adott nevek fogják a hívás során megadott értékeket tartalmazni. A lokális változók kezdeti értéke az üres szöveg. Egy függvénynek nem lehet két azonos nevű paramétere.

A body-of-function awk kifejezéseket tartalmaz. Ez a függvény legfontosabb része, mivel ez adja meg, hogy a függvénynek mit is kell csinálnia. Az argumentumok egy bizonyos fajta kommunikációt biztosítanak a program többi részével; a lokális változók ideiglenes tárolási helyet biztosítanak.

Az argumentumok és a lokális változók nem különböztethetők meg szintaktikailag; ehelyett a hívás során megadott argumentumok száma határozza meg, hogy mennyi argumentuma lesz a függvénynek. Így, ha három argumentummal hívjuk meg a függvényt, akkor az első három név a parameter-list listában argumentum lesz, a többi lokális változó.

Ebből következik, hogy ha nem mindig ugyanannyi argumentummal hívjuk meg a függvényt, akkor egyes nevek a parameter-list listában néha argumentumok lesznek, néha lokális változók. Egy másik felfogás szerint, a meg nem adott argumentumok kezdeti értéke az üres szöveg lesz.

Általában amikor a függvényt írod, akkor már tudod, hogy mennyi argumentumot akarsz használni és mennyi lokális változót, így az a szokás, hogy néhány extra szóköz karaktert kell az argumentumok és a lokális változók közé tenni, hogy ezzel dokumentáld a funkcióbeli különbséget.

A függvény futtatása során az argumentumok és lokális változók "eltakarják" a programod más részein használt azonos nevű változókat. Az "eltakart" változók nem elérhetőek a függvényben, mivel a nevük az adott függvényben valami mást jelentenek. Minden más változót, amit az awk programban használtál el lehet érni és megváltoztatni a függvényen belül.

Az argumentumok csak a függvény belsejében érvényesek. Ha a függvény befejezte a működését és kilépett, akkor újra el tudod érni a függvény által "eltakart" változókat.

A függvény maga is tartalmazhat olyan kifejezéseket, amelyek másik függvényt hívnak meg. A függvény még saját magát is meghívhatja, közvetlenül vagy indirekt módon. Ekkor a függvényt rekurzívnak hívjuk.

A legtöbb awk implementációban, a gawk-ban is, a function kulcsszót rövidíteni lehet a func szóval. Ugyanakkor a POSIX szabvány csak a function kulcsszót definiálja. Ennek az a praktikus következménye, hogy ha a gawk POSIX kompatíbilis módban fut (see section Command Line Options), akkor az alábbi kifejezés nem definiál egy függvényt:

func foo() { a = sqrt($1) ; print a }

Ehelyett egy szabályt generál minden rekordra, ami összefűzi a `func' változó tartalmát a `foo' függvény visszatérési értékével. Ha az eredmény szöveg nem üres, akkor végrehajtja a tevékenységet. Nem valószínű, hogy ezt akartad. (Az awk elfogadja ezt a programot, mint egy szintaktikailag helyes program, mivel a függvényt azelőtt lehet használni, mielőtt a függvény definiálva lett.)

Ha hordozható awk programot akarsz írni, akkor mindig a function kulcsszót használd a függvények definíciójánál.

Példák a függvénydefinícióra

Itt egy példa a felhasználó által definiálható függvényekre, myprint, ami egy számot kap argumentumként, és azt egy adott formában kinyomtatja.

function myprint(num)
{
     printf "%6.3g\n", num
}

Itt pedig bemutatjuk, hogy egy awk szabály hogyan használná a myprint függvényt:

$3 > 0     { myprint($3) }

Ez a program kinyomtatja minden bemeneti rekord harmadik mezőjét az általunk megadott formátumban, ha a mező pozitív számot tartalmaz. Így, ha a bemenet ez:

 1.2   3.4    5.6   7.8
 9.10 11.12 -13.14 15.16
17.18 19.20  21.22 23.24

akkor a program, az általunk megadott függvénnyel ezt fogja kinyomtatni:

   5.6
  21.2

Ez a függvény kitörli egy tömb minden elemét.

function delarray(a,    i)
{
    for (i in a)
       delete a[i]
}

Amikor tömbökkel dolgozunk, gyakran szükség lehet egy tömb minden elemének a kitörlésére, és új elemekkel újrakezdeni (see section A delete kifejezés). Ahelyett, hogy a törlő ciklust minden alkalommal le kellene írni, elég a fent definiált delarray függvényt meghívni.

Itt egy példa a rekurzív függvényre. A függvény egy szöveget vár argumentumként, és fordítva nyomtatja azt ki.

function rev(str, start)
{
    if (start == 0)
        return ""

    return (substr(str, start, 1) rev(str, start - 1))
}

Ha ez a függvény egy `rev.awk' nevű file-ban van, akkor így tesztelhetjük:

$ echo "Don't Panic!" |
> gawk --source '{ print rev($0, length($0)) }' -f rev.awk
-| !cinaP t'noD

Itt egy példa, ami az strftime beépített függvényt használja (see section Dátumfeldolgozó függvények). A ctime függvény a C nyelvben egy időbélyeget vár argumentumként, és egy jól ismert formában nyomtatja azt ki. Itt az awk verziója:

# ctime.awk
#
# awk version of C ctime(3) function

function ctime(ts,    format)
{
    format = "%a %b %d %H:%M:%S %Z %Y"
    if (ts == 0)
        ts = systime()       # use current time as default
    return strftime(format, ts)
}

A felhasználó által definiált függvények hívása

Egy függvény hívása azt jelenti, hogy a függvény lefut, és elvégzi a munkáját. A függvényhívás egy kifejezés, és az értéke a függvény visszatérési értéke.

A függvényhívás a függvény nevéből és az utána álló, zárójelek közti argumentumok listájából áll. Amit argumentumként megadsz, azok is awk kifejezések; minden függvényhívásnál az argumentumként adott kifejezések kiértékelődnek, és az értékük lesz az aktuális argumentum. Például itt a foo függvényt hívjuk meg három argumentummal (ahol az első egy szövegösszefűzés):

foo(x y, "lose", 4 * z)

Figyelem: szóköz és tabulátor karakterek nem megengedettek a függvény neve és a nyitó zárójel között. Ha mégis lenne szóköz vagy tabulátor karakter a függvény neve és a nyitó zárójel között, akkor az awk ezt úgy is értelmezheti, mintha összefűzést szeretnél csinálni a függvény nevével és a zárójelek között megadott kifejezéssel. Ugyanakkor észreveszi, hogy függvény nevet adtál meg, nem változót, és így hibát fog jelezni.

Amikor a függvény meghívódik, akkor az argumentumként megadott értékek egy másolatát kapja meg. Ezt a módszert úgy is hívják, hogy hívás érték alapján. A függvényt hívó kifejezés akár változót is használhat argumentumként, de a hívott függvény ezt nem tudja: csak annyit tud a meghívott függvény, hogy az argumentum értéke micsoda. Például, ha ezt a kódot írod:

foo = "bar"
z = myfunc(foo)

akkor nem szabad azt gondolnod, hogy a myfunc függvény argumentuma a "foo változó", hanem hogy az argumentum a "bar" szöveg.

Ha a myfunc függvény megváltoztatja a lokális változók értékét, akkor az semmilyen hatással nincs más változókra. Így ha a myfunc függvény ezt csinálja:

function myfunc(str)
{
  print str
  str = "zzz"
  print str
}

akkor bár az első argumentumát, str, megváltoztatja, de a foo értéke nem fog megváltozni a hívó kifejezésben. A foo szerepe a myfunc függvény hívása során akkor ér véget, amikor az értékét, "bar", kiszámolta. Ha az str a myfunc függvényen kívül is létezik, akkor a függvény ennek a külső változónak az értékét nem tudja megváltoztatni, mivel a myfunc függvény futtatása ezt a változót "eltakarja".

Ugyanakkor ha a függvény argumentuma egy tömb, akkor az nem másolódik. Ehelyett, a függvény magát az eredeti tömböt tudja megváltoztatni. Ezt úgy nevezik, hogy hívás referencia alapján. A függvény belsejében végrehajtott változások egy tömbön, a függvényen kívül is érvényben lesznek. Ez nagyon veszélyes lehet, ha nem figyelsz oda, hogy mit csinálsz. Például:

function changeit(array, ind, nvalue)
{
     array[ind] = nvalue
}

BEGIN {
    a[1] = 1; a[2] = 2; a[3] = 3
    changeit(a, 2, "two")
    printf "a[1] = %s, a[2] = %s, a[3] = %s\n",
            a[1], a[2], a[3]
}

Ennek a programnak az eredménye: `a[1] = 1, a[2] = two, a[3] = 3', mivel a changeit függvény a "two" szöveget tárolja el az a tömb második elemében.

Néhány awk implementáció megengedi nem definiált függvények hívását, és csak a futtatás során jeleznek hibát, amikor a függvényt megpróbálják lefuttatni. Például:

BEGIN {
    if (0)
        foo()
    else
        bar()
}
function bar() { ... }
# figyelem: `foo' nem definiált

Mivel az `if' kifejezés soha nem igaz, ezért nem igazán probléma, hogy a foo függvény nem lett definiálva. Általában azért ez probléma, ha a program olyan függvényt akar meghívni, ami nem lett definiálva.

Ha a `--lint' opció szerepel a parancssorban (see section Command Line Options), akkor a gawk jelenteni fogja a nem definiált függvényeket.

Néhány awk implementáció hibát generál, ha a next kifejezést (see section A next kifejezés) használod egy általad definiált függvényben. A gawk-nak nincs ilyen problémája.

A return kifejezés

A felhasználó által definiált függvények tartalmazhatják a return kifejezést. Ennek hatására a függvényből kilép, és folytatja az awk program futtatását a függvény hívása után. Visszatérési érték is megadható. Így néz ki:

return [expression]

Az expression rész opcionális. Ha nincs megadva, akkor a visszatérési érték nem definiált, és ezért nem megjósolható.

Minden függvénydefiníció végén egy egyszerű, argumentumok nélküli return kifejezést lehet feltételezni. Így ha a program eléri a függvény végét, akkor a függvény egy megjósolhatatlan értékkel visszatér. Az awk nem fog figyelmeztetni, ha egy ilyen függvény visszatérési értékét használod.

Néha azért írsz egy függvényt, amit csinál, és nem azért amit visszaad. Ezek a C nyelvben a void függvények, a Pascal-ban a procedure-k. Ezért hasznos, hogy nem adsz meg semmilyen visszatérési értéket; egyszerűen csak tartsd fejben, hogy ha egy ilyen függvény visszatérési értékét használod, akkor azt a saját felelősségedre teszed.

Itt egy példa, amelyik egy tömbben található legnagyobb számmal tér vissza:

function maxelt(vec,   i, ret)
{
     for (i in vec) {
          if (ret == "" || vec[i] > ret)
               ret = vec[i]
     }
     return ret
}

A maxelt függvényt egy argumentummal kell meghívni, ami a tömb neve. Az i és a ret lokális változók, és nem argumentumok; bár semmi nincs ami meggátoljon abban, hogy egy második és egy harmadik argumentumot is megadj a függvénynek, de ekkor az eredmény furcsa is lehet. Az extra szóközök az i előtt jelzik, hogy ezután a lokális változókat definiálod. Ezt a konvenciót érdemes követned.

Itt egy program, amelyik a maxelt függvényt használja. A program betölt egy tömböt, meghívja a maxelt függvényt, majd kinyomtatja a tömb legnagyob elemét:

awk '
function maxelt(vec,   i, ret)
{
     for (i in vec) {
          if (ret == "" || vec[i] > ret)
               ret = vec[i]
     }
     return ret
}

# Load all fields of each record into nums.
{
     for(i = 1; i <= NF; i++)
          nums[NR, i] = $i
}

END {
     print maxelt(nums)
}'

Az alábbi bemenetre:

 1 5 23 8 16
44 3 5 2 8 26
256 291 1396 2962 100
-6 467 998 1101
99385 11 0 225

a program azt fogja mondani (megjósolható módon), hogy 99385 a legnagyobb szám a tömbben.


Go to the first, previous, next, last section, table of contents.