A Miranda programozási nyelv

Magasabb rendű függvények

Magasabb rendű függvények

Az olyan függvényeket, melyek megengednek függvényt aktuális paraméterként, vagy eredményül függvényt adnak, magasabb rendű függvényeknek nevezzük. Ellentétben az elsőrendű függvényekkel, melyek csak értéket engednek meg argumentumként és eredményként. Például egy magasabb rendű függvény, mely megenged függvény argumentumot (megadja, hogy mi a függvény értéke a 0 pontban):

atzero:: (num->num)->num
atzero f = f 0

Ekkor lehetséges kezdeti kifejezések: atzero inc, atzero sq.
Egy n argumentumú függvény leegyszerűsíthető egy olyan magasabb rendű függvényre, mely egy függvényt ad eredményül. Most ezt az új függvényt kell alkalmazni a következő argumentumra, és így tovább, míg végül mind az n argumentumot kezeltük egy argumentumú magasabb rendű függvényekkel.
A többargumentumú függvények definíciója a Mirandában: függvény-név arg1 arg2 ... argn = kifejezés
Minden függvényt a fenti módszerrel kezelnek, így ez a definíció a következőképpen olvasható: (...((függvény-név arg1)arg2) ... argn) = kifejezés
A függvény típusa pedig: függvény-név:: A1->(A2-> ... ->(An->B) ... ),
mivel pedig a -> nyíl jobb asszociatív, így írható: függvény-név:: A1->A2-> ... An->B
A lebontási séma lehetővé teszi, hogy az n argumentumúnak definiált függvényt akár n-nél kevesebb argumentummal is használhassuk, így ún. paraméterezett függvényt kapjunk. A lebontás nélkül az n argumentumúnak definiált függvényt csak akkor lehet alkalmazni, ha mind az n argumentum rendelkezésre áll. Lebontás esetén viszont néhány argumentum lehet kitöltetlen, és az így eredményül kapott paraméterezett függvényt át lehet adni egy másik függvénynek, mely kitölti a megmaradó argumentumokat. Például:

plus:: num->num->num
plus x y = x+y

Ez ekvivalens a következővel:

plus:: num->(num->num)
(plus x) y = x+y

Ekkor a plus 1 kezdeti kifejezéshez kell még egy argumentum, hogy a függvénytörzs összeadása végrehajtható legyen. A kezdeti kifejezés eredménye egy egy argumentumú függvény, melynek típusa num->num, és a neve nem meghatározott. Ekkor a plus definícióját felhasználva az inc függvény a kétféleképpen is definiálható:

incr:: num->num
incr x = plus 1 x

ill. a paraméterezett verzióval:

incr = plus 1

A két definíció közti egyetlen különbség csupán az, hogy a második tömörebb. Nagyon jól használható ez a lehetőség, hogy új függvény hozható létre oly módon, hogy egy jóval általánosabb függvény paramétereit megadjuk. Egy másik példa a map függvény, mely az első argumentumként kapott num->num típusú függvényt a második argumentumban található lista minden elemére alkalmazza.

map:: (num->num)->[num]->[num]
map f [] = []
map f (x : xs) = f x : map f xs

Így egy olyan függvény, mely a lista elemeit eggyel megnöveli:

mapincr:: [num]->[num]
mapincr = map incr    vagy    mapincr = map (plus 1)

Ekkor egy kiértékelés:

mapincr [1,2,3]
 -> map (plus 1) [1,2,3]
 -> map (plus 1) (1 : 2 : 3 : [])
 -> plus 1 1 : map (plus 1) (2 : 3 : [])
 -> 2 : map (plus 1) (2 : 3 : [])
 -> 2 : (plus 1) 2 : map (plus 1) (3 : [])
 -> 2 : 3 : map (plus 1) (3 : [])
 -> 2 : 3 : (plus 1) 3 : map (plus 1) []
 -> 2 : 3 : 4 : map (plus 1) []
 -> 2 : 3 : 4 : [] = [2,3,4]

Még egy példa: a member egy könyvtári függvény esetében a member x a ellenőrzi, hogy az x lista tartalmazza-e az a elemet (igaz/hamis visszatérési érték). Azonban a member részleges alkalmazásával több hasznos állítást kifejezhetünk, például:

vowel = member ['a','e','i','o','u']    || magánhangzók
digit = member ['0','1','2','3','4','5','6','7','8','9']    || számjegyek
month = member ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Okt","Nov","Dec"]    || hónapok

A magasabb rendű programozás további példájaként lássuk a foldr függvényt:

foldr op k [] = k
foldr op k (a:x) = op a (foldr op k x)

Az összes szabványos lista-művelet megvalósítható a foldr függvény részleges alkalmazásával. Például:

sum = foldr (+) 0
product = foldr (*) 1
and = foldr (&) True
or = foldr (\/) False
reverse = foldr postfix []
          where postfix a x = x ++ [a]