• Nem Talált Eredményt

Magasabb rendű függvények

Haskellben a magasabb rendű függvények olyan függvények, amelyek-nek bemeneti paraméterük és kimeneti értékük is lehet függvény. Angolul higher-order function a megnevezésük.

4.21. feladat Írjunk egy Haskell-függvényt, amely a paraméterként meg-adott függvényt duplán alkalmazza egy megadott bemeneten.

duplaz :: (a -> a) -> a -> a duplaz fg x = fg (fg x)

> duplaz (\ x -> x + 1) 10 12

> duplaz sqrt 2 1.189207115002721

A duplaz magasabb rendű függvény, mert az fgparaméter egy függvény.

Működés szempontjából az fgfüggvényt kétszer alkalmazza a második pa-raméterére.

A Haskellben több könyvtárfüggvény is létezik, amely magasabb rendű függvény, ilyenek például a map, filter, foldr, foldl stb. A továb-biakban a map és a filter függvények működését tárgyaljuk, a többi függvényre a jegyzet későbbi fejezeteiben kerül sor.

Amapfüggvénynek két argumentuma van: a második egy lista, az első pedig egy függvény, amelyet alkalmaz a lista minden elemére.

4.22. feladat Írjunk egy olyan Haskell-lekérdezést, amely meghatározza a paraméterként megadottDoubleelemtípusú lista elemeinek négyzetgyökét.

> map sqrt [3.0, 4.0, 5.0, 6.0, 7.0]

[1.7320508075688772, 2.0,2 .23606797749979, 2.449489742783178, 2.6457513110645907]

4.9. Magasabb rendű függvények 59 Az előző fejezetben megírtnovelLs,novelLsP, illetvevaltakoz függ-vények is megadhatók map-et használva. Figyeljük meg, hogy a függvények lista típusú bemeneti paraméterei nincsenek feltüntetve. Haskellben egy függvény utolsó paraméterei ugyanis elhagyhatók, ha a törzsében is, szá-mításba véve a sorrendet, utolsó paraméterként jelennek meg.

novelLsMap :: (Num a) => [a] -> [a]

novelLsMap = map (+ 1)

novelLsPMap :: (Num a) => a -> [a] -> [a]

novelLsPMap p = map (+ p)

valtakozMap :: Integral a => a -> [Bool]

valtakozMap n = map even [0..n-1]

4.23. feladat Írjunk egy olyan Haskell-lekérdezést, amely meghatározza minden listabeli elemre a páros osztók listáját.

parosO :: (Integral a) => a -> (a, [a])

parosO n = (n, [i | i <- [2, 4..div n 2], mod n i == 0])

> parosO 60

(60,[2,4,6,10,12,20,30])

Első lépésként írtunk egyparosOfüggvényt, amely egy kételemű tuple típu-sú értéket határozott meg, ahol a tuple első eleme maga a bemenet, második eleme pedig egy lista, amelyben a bemenet páros osztóit generáltuk ki. Ha a bemenetnek nincs páros osztója, akkor az eredmény üres lista lesz.

Második lépésként meghívjuk a mapfüggvényt úgy, hogy első paramé-tere apárosOfüggvény, második pedig egy lista legyen, amelybe azokat az számokat írjuk, amelyeknek a páros osztóit szeretnénk meghatározni.

A következő lekérdezés megadja az 50és 59közötti számok páros osz-tóinak listáját.

> map parosO [50..59]

[(50,[2,10]),(51,[]),(52,[2,4,26]),(53,[]),(54,[2,6,18]), (55,[]),(56,[2,4,8,14,28]),(57,[]),(58,[2]),(59,[])]

A filter függvénynek is két argumentuma van: a második egy lista, az első pedig egy logikai függvény, amely alapján kiválasztásra kerülnek a listabeli elemek.

4.24. feladat Írjunk egy Haskell-függvényt, amely kiválasztja egy lista ele-mei közül a páros számokat.

parosLs :: (Integral a) => [a] -> [a]

parosLs = filter even

> parosLs [1..20]

[2,4,6,8,10,12,14,16,18,20]

Bemenetként általunk megírt logikai függvényt is megadhatunk. Pél-dául a négyzetszámok listáját a következőképpen is kigenerálhatjuk, ahol alkalmazni fogjuk a korábban megírt negyzetVfüggvényt:

> filter negyzetV [1..100]

[1,4,9,16,25,36,49,64,81,100]

A magasabb rendű függvényeknek van egy másik fontos jellemzőjük is:

részlegesen lehet paraméterezni őket. Ezt curryzésnek is mondjuk, Haskell Brooks Curry matematikus után. A részleges paraméterezés azt jelenti, hogy a függvényhívás megengedettlátszólag kevesebb paraméterrel is.

Például ha a páratlan prímszámok listáját szeretnénk kigenerálni100 -ig, akkor afilterés a korábban megírtprimTrészleges paraméterezésével ezt egy egysoros lekérdezésben megtehetjük:

> filter (primT 3) [3,5..100]

[3,5,7,11,13,17,19,...,83,89,97]

4.25. feladat Írjunk egy Haskell-függvényt, amely az x és k bemenetekre, ahol kegész szám, meghatározza azx0, x1, . . ., xk értékeket.

fugv1 :: (Integral a, Num b) => b -> a -> [b]

fugv1 x k = map (x ^) [0..k]

> fugv1 7 6

[1,7,49,343,2401,16807,117649]

Az implementáció során a map könyvtárfüggvényt használtuk, ahol a map függvény első paraméterét a hatványozó operátort, infix formában, részle-gesen paraméterezve adtuk meg.

4.26. feladat Írjunk egy Haskell-függvényt, amely az x és k bemenetekre meghatározza az 0k, 1k, . . ., xk értékeket.

4.9. Magasabb rendű függvények 61 fugv2 :: (Integral a, Num b, Enum b) => b -> a -> [b]

fugv2 x k = map (^ k) [0..x]

> fugv2 7 6

[0,1,64,729,4096,15625,46656,117649]

Vegyük észre, hogy a feladat most különböző alapú, de ugyanolyan hat-ványkitevőn levő értékeket kell kiszámoljon. Ezért a ^ operátort megint infix formában hívtuk úgy, hogy a ^ operátor első argumentuma rendre a [0..x]lista elemei legyenek.

Megjegyezzük azt is, hogy ha módosítjuk a zárójelezést, akkor a^ ope-rátor prefix formában kerül meghívásra, ami más eredményt fog adni. A következő kód a k0, k1, . . ., kx értékeket határozza meg.

fugv2_ :: (Integral a, Num b) => a -> b -> [b]

fugv2_ x k = map ((^) k) [0..x]

> fugv2_ 7 6

[1,6,36,216,1296,7776,46656,279936]

A következőkben másképp járunk el. A beépített operátor helyett meg-írjuk a saját hatványozó függvényeinket, és amapfüggvénynek ezeket adjuk meg paraméternek.

A myPow1 függvény tulajdonképpen a gyorshatványozás algoritmusa, első paramétere az alap, második a hatványkitevő lesz, ahol a rekurzív hí-vásra a where kifejezésben kerül sor. A rekurzív hívás eredményét atemp fogja jelölni, amelyet az őrfeltételekben szorzótényezőként használunk. A szükséges szorzásokat az határozza meg, hogy a hatványkitevő páros vagy páratlan szám.

myPow1 :: (Integral a) => a -> a -> a myPow1 x n

| n < 0 = error "Negativ kitevo"

| n == 0 = 1

| even n = temp * temp

| otherwise = x * temp * temp where

temp = myPow1 x (div n 2)

A következő függvényekben háromféleképpen kerül kiértékelésre a myPow függvény. A fugvA-ban prefix formában használjuk, hogy az

x0, x1, . . ., xk értékeket tudjuk meghatározni.

fugvA :: Integral a => a -> a -> [a]

fugvA x k = map (myPow1 x) [0..k]

> fugvA 7 6

[1,7,49,343,2401,16807,117649]

AfugvB-ben infix formában alkalmazzuk amyPow1függvényt, azért, hogy

a 0k, 1k, . . ., xk értékek kerüljenek kiértékelésre.

fugvB :: Integral a => a -> a -> [a]

fugvB x k = map (‘myPow1‘ k) [0..x]

> fugvB 7 6

[0,1,64,729,4096,15625,46656,117649]

A fugvC-ben alkalmazásra kerül a beépített flip függvény, amely se-gítségével a paraméterek sorrendjét lehet megváltoztatni, így most is a

0k, 1k, . . ., xk értékeket határozza meg a kiértékelés.

fugvC :: Integral a => a -> a -> [a]

fugvC x k = map (flip myPow1 k) [0..x]

> fugvC 7 6

[0,1,64,729,4096,15625,46656,117649]

Aflipfüggvény megértéséhez figyeljük meg a következő kiértékelések ered-ményeit.

> myPow1 2 10 1024

> flip myPow1 2 10 100

A következő myPow2függvény is azxn értékét határozza meg, de algo-ritmikailag különbözik amyPow1-től. Az alap négyzetre emelését a rekurzív függvényhíváskor valósítja meg, illetve a paraméterek sorrendjét is felcserél-tük.

myPow2 :: (Integral a, Num b) => a -> b -> b myPow2 n x

| n < 0 = error "negativ kitevo"

| n == 0 = 1

| even n = temp

| otherwise = x * temp where

temp = myPow2 (div n 2) (x * x)

4.10. Függvénykompozíció 63 Az olvasóra bízzuk, hogy az előzőfugvA, fugvB, illetvefugvCfüggvények mintájára, egy map-ben részlegesen paraméterezve alkalmazza amyPow2-t.

4.27. feladat Írjunk egy Haskell-függvényt, amely kiválasztja egy lista ele-mei közül az x-szel osztható számokat.

fugv3 :: (Integral a) => a -> [a] -> [a]

fugv3 x ls = filter (oszthato x) ls where

oszthato :: (Integral a) => a -> a -> Bool oszthato x y = mod y x == 0

> fugv3 7 [1..100]

[7,14,21,28,35,42,49,56,63,70,77,84,91,98]

Az oszthatofüggvénnyel korábban is foglalkoztunk. A függvény megvizs-gálja, hogy xosztója-ey-nak, ahol a függvénytörzset a korábbi változathoz képest egysorosra módosítottuk, és felcseréltük a paraméterek szerepét.

4.10. Függvénykompozíció

A függvénykompozíció a matematikából jól ismert művelet. A Haskell-ben lehetőségünk van hasonló művelet definiálására, jelölésére a pont (.) szimbólumot használjuk. A következő példában két könyvtárfüggvény kom-pozíciójával dolgozunk, a not-tal és azeven-nel.

4.28. feladat Írjunk egy Haskell-függvényt, amely kiválasztja egy lista ele-mei közül a páratlan számokat.

paratlanLs :: (Integral a) => [a] -> [a]

paratlanLs = filter (not . even)

> paratlanLs [1..20]

[1,3,5,7,9,11,13,15,17,19]

A korábbi duplaz függvény is megadható függvénykompozíciót alkal-mazva:

duplaz_ :: (a -> a) -> a -> a duplaz_ fg = fg . fg

> duplaz_ (+1) 10 12

A következő példában két könyvtárfüggvény kompozíciójával fogunk dolgozni, alkalmazzuk az inités tailkönyvtárfüggvényeket.

4.29. feladat Írjunk egy Haskell-függvényt, amely levágja a paraméterként megadott lista első és utolsó elemét.

levag :: [a] -> [a]

levag = init . tail

> levag "gezakekazeg"

"ezakekaze"

4.30. feladat Írjunk egy Haskell-függvényt, amely a paraméterként meg-adott 1-nél nagyobb természetes számokat tartalmazó listából kiválogatja az összetett számokat.

Az összetett számok kiválogatását az osszetett függvény végzi, a notésprimTfüggvényeken függvénykompozíciót alkalmazva, ahol aprimT függvényt korábban implementáltuk:

osszetett :: (Integral a) => [a] -> [a]

osszetett = filter (not . primT 3)

> osszetett [2..20]

[4,6,8,9,10,12,14,15,16,18,20]