Funkcionális programozás 2. el adás Sapientia Egyetem, Matematika-Informatika Tanszék Marosvásárhely, Románia mgyongyi@ms.sapientia.ro 2019, tavaszi félév
Mir l volt szó? Követelmények, osztályozás Programozási módszerek: imperatív, funkcionális, logikai programozási módszerek, Haskell: történelmi háttér, bevezet fogalmak, az els program Alaptípusok Osztálytípusok
Mir l lesz szó? A Haskell programozási nyelv f bb jellemz i: megjegyzések használata feltételek megadása szigorú, statikus típusosság rekurzió margó szabály mintaillesztés halmazkifejezések lambda kifejezések magasabb rend függvények magasabb rend függvények részleges paraméterezése feltételes kifejezések GHC parancsok
Megjegyzések használata egysoros megjegyzés: --ez egy egysoros megjegyzés több soros megjegyzés: {- ez egy több soros megjegyzés -}
Feltételek megadása Feltételek megadása (denition by cases) : a függvény értékét az els, igaz feltételhez tartozó kifejezés adja, amely kifejezést az aktuális paraméter értéke alapján értékeljük ki. 1. feladat Határozzuk meg egy szám el jelét. elojel :: Int -> Int elojel x x < 0 = -1 x > 0 = 1 x == 0 = 0 elojel1 :: Int -> String elojel1 0 = "nulla" elojel1 x x < 0 = "negatív" x > 0 = "pozitív" a függvény kiértékelése: > elojel (-10) ÁRTON Gyöngyvér
Típusdeníciók Szigorú statikus típusosság (strong, static type system): a kifejezések típusát a fordítóprogram meghatározza úgy is, ha azok nincsenek feltüntetve, elemi hibák detektálása, típusosztályok alkalmazása. 2. feladat Határozzuk meg egy szám abszolút értékét, 1. változat. abszolut1 x x < 0 = -x otherwise = x a függvény kiértékelhet, egész és valós típusú bemenetre is: > abszolut1 (-10) 10 > abszolut1 (-10.0) 10.0
Típusdeníciók 3. feladat Határozzuk meg egy szám el jelét, 2. változat. A típusdenícióval lesz kíthetjük a függvényargumentumok típusát: csak Int típusúak lehetnek a következ függvényben. abszolut2 :: Int -> Int abszolut2 x x < 0 = -x otherwise = x a függvény kiértékelése: > abszolut2 (-10) 10 > abszolut2 (-10.0) No instance for (Fractional Int) arising from the literal '10'...
Típusdeníciók 4. feladat Határozzuk meg egy szám el jelét, 3. változat. A típusdenícióval lesz kíthetjük a függvényargumentumok típusát: Float típusra. abszolut3 :: Float -> Float abszolut3 x x < 0 = -x otherwise = x a függvény kiértékelése: > abszolut3 (-10.0) 10.0 > abszolut3 (-10) 10.0
Típusdeníciók 5. feladat Határozzuk meg egy szám el jelét, 4. változat. A típusdenícióban típusosztályokat adunk, így a függvényargumentumok többfélék lehetnek. abszolut4 :: (Num a, Ord a) => a -> a abszolut4 x x < 0 = -x otherwise = x a függvény kiértékelése: > abszolut4 (-10.0) 10.0 > abszolut4 (-10) 10
Rekurzió (recursion) A rekurzió a funkcionális nyelvek alapvezérlési szerkezete, a függvények hivatkozhatnak önmagukra és kölcsönösen egymásra. 6. feladat Határozzuk meg két egész szám legnagyobb közös osztóját. Kivonásos módszer lnko :: Int -> Int -> Int lnko a b a > b = lnko (a-b) b a < b = lnko a (b-a) otherwise = a Eukleidész módszere euklid :: Integral a => a -> a -> a euklid a b b == 0 = a otherwise = euklid b (rem a b) a függvény kiértékelése: > lnko 24 204 ÁRTON Gyöngyvér
Margó szabály (layout rule) Az összetartozó kifejezéseket a baloldali margó alapján lehet megállapítani. 7. feladat Határozzuk meg egy másodfokú egyenlet valós gyökeit. gyok :: (Fractional a, Floating a, Ord a) => a -> a -> a -> (a, a) gyok a b c delta < 0 = error "Komplex gyokok" otherwise = (x1, x2) where x1 = (-b + sqrt delta) / n x2 = (-b - sqrt delta) / n delta = b * b - 4 * a * c n = 2 * a delta a = 3 * a a függvény kiértékelése: > gyok 2 3 1 Melyik delta kifejezés értékel dik ki? > delta 2
Mintaillesztés Az argumentumok mintaillesztése (patterns denition): a függvényértékét az a függvénytörzs határozza meg, amelyre a formális paraméter egy megadott minta alapján illeszkedik. A mintaillesztést és a feltételek megadását lehet együttesen is alkalmazni. 8. feladat Határozzuk meg egy szám számjegyeinek összegét, szorzatát. szosszeg :: Int -> Int szosszeg 0 = 0 szosszeg x = ( x `rem` 10 ) + szosszeg (x `div` 10) a függvény kiértékelése: > szosszeg 1234 szszorzat :: Int -> Int szszorzat 0 = 0 szszorzat x x < 10 = x otherwise = ( x `rem` 10 ) * szszorzat (x `div` 10)
Mintaillesztés 9. feladat Határozzuk meg egy 3 elem lista elemeinek összegét. osszeg :: [Int] -> Int osszeg [] = 0 osszeg [x] = x osszeg [x,y] = x + y osszeg [x,y,z] = x + y + z a függvény kiértékelése: > osszeg [10, 4, 2] Mi történik a 4, 5 stb elem listák esetében? Futási hiba adódik, ezek az esetek nincsenek letárgyalva. > osszeg [10, 4, 2, 8]
Halmazkifejezések (list comprehension) Iteratív adatszerkezetek (listák, halmazok, sorozatok) elemeinek megadására alkalmazott jelölésrendszer. 10. feladat Határozzuk meg a paraméterként megadott szám osztóit. osztok :: Int -> [ Int ] osztok n = [ i i <- [1..n], n `rem` i==0] a függvény kiértékelése: > osztok 60 > length (osztok 60)
Lambda kifejezések 11. feladat Növeljük a paraméterként megadott szám értékét 1-el. my_inc :: (Num a) => a -> a my_inc = \x -> x + 1 > my_inc 23.5 24.5 A függvények egy alternatív deniálási módja.
Lambda kifejezések 12. feladat Növeljük a paraméterként megadott számok értékét 1-el. l_inc :: (Num a) => [a] -> [a] l_inc ls = map (\x -> x + 1) ls > l_inc [1..10] [2,3,4,5,6,7,8,9,10,11]
Magasabb rend függvények (high order function) argumentumuk lehet függvény, és visszatérítési értékük is lehet függvény, könyvtárfüggvények: map, lter, foldr, foldl, stb. map: két argumentuma van, az els egy függvény, amelyet alkalmaz a listaként megadott második argumentumára. 13. feladat Határozzuk meg a paraméterként megadott számok négyzetgyökét. > map sqrt [ 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 ] [1.7320508075688772,2.0,2.23606797749979,2.449489742783178, 2.6457513110645907,2.8284271247461903,3.0]
Magasabb rend függvények 14. feladat Határozzuk meg a paraméterként megadott számok közül a páros számokat. paroslista :: (Integral a) => [a] -> [a] paroslista ls = filter even ls > paroslista [1..20] [2,4,6,8,10,12,14,16,18,20]
Magasabb rend függvények 15. feladat Növeljük a paraméterként megadott szám értékét kett vel. duplaz :: (a -> a) -> a -> a duplaz f x = f (f x) my_inc :: (Num a) => a -> a my_inc x = x + 1 > duplaz my_inc 10 12 A duplaz els paramétere egy függvény, amit kétszer alkalmaz a második paraméterére, a my_inc a megadott paraméter értékét 1-el növeli.
Magasabb rend függvények részleges paraméterezése partial parameterization, curry-zés, Haskell Curry matematikus után, a függvényhívás megengedett kevesebb paraméterrel is. 16. feladat Határozzuk meg egy x 0, x 1,..., x 10 (a kitev változik). hatv1 :: (Integral a) => a -> a -> a hatv1 x n n < 0 = error "Negativ kitevo" n == 0 = 1 mod n 2 == 0 = temp * temp otherwise = x * temp * temp where temp = hatv1 x (div n 2) fugv1 :: (Integral a) => a -> [a] fugv1 x = map (hatv1 x) [0..10] > fugv1 5 [1,5,25,125,625,3125,15625,78125,390625,1953125,9765625]
Magasabb rend függvények részleges paraméterezése 17. feladat Határozzuk meg 0 n, 1 n,..., 10 n (az alap változik). hatv2 :: (Integral a) => a -> a -> a hatv2 n x n < 0 = error "Negativ kitevo" n == 0 = 1 mod n 2 == 0 = temp * temp otherwise = x * temp * temp where temp = hatv2 (div n 2) x fugv2 :: (Integral a) => a -> [a] fugv2 n = map (hatv2 n) [0..10] > fugv2 5 [0,1,32,243,1024,3125,7776,16807,32768,59049,100000]
Magasabb rend függvények részleges paraméterezése 18. feladat Határozzuk meg egy alap 0, alap 1,..., alap 10 (a kitev változik). fugv3 :: (Integral a) => a -> [a] fugv3 alap = map ((\x n -> x ^ n) alap) [0..10] > fugv3 5 [1,5,25,125,625,3125,15625,78125,390625,1953125,9765625]
Magasabb rend függvények részleges paraméterezése 19. feladat Határozzuk meg 0 kit, 1 kit,..., 10 kit (az alap változik). fugv4 :: (Integral a) => a -> [a] fugv4 kit = map ((\n x -> x ^ n) kit) [0..10] > fugv4 5 [0,1,32,243,1024,3125,7776,16807,32768,59049,100000]
Magasabb rend függvények részleges paraméterezése 20. feladat Válasszuk ki egy adott listából az x-el osztható számokat, használjuk a filter könyvtárfüggvényt: oszthato :: (Integral a) => a -> a -> Bool oszthato x y mod y x == 0 = True oszthato x y = mod y x == 0 otherwise = False fugv :: (Integral a) => a -> [a] -> [a] fugv x ls = filter (oszthato x) ls > fugv 7 [1..100] [7,14,21,28,35,42,49,56,63,70,77,84,91,98]
Feltételes kifejezések 21. feladat Vizsgáljuk meg hogy x osztható-e y-al. oszthato1 :: (Integral a) => a -> a -> Bool oszthato1 x y = if mod y x == 0 then True else False 22. feladat Határozzuk meg n!. faktorialis :: Integer -> Integer faktorialis n = if n == 0 then 1 else n * faktorialis4 (n-1) > faktorialis 100 Az if a Haskell-ben nem utasítás (vagy állítás), hanem egy feltételes kifejezés, ezért az else ág kötelez.
Feltételes kifejezések 23. feladat Határozzuk meg x n. hatv3 :: (Integral a) => a -> a -> a hatv3 x n = if n < 0 then error "negativ kitevo" else if n == 0 then 1 else if mod n 2 == 0 then hatv3 (x*x) (div n 2) else x * hatv3 (x*x) (div n 2) > hatv3 2 100 1267650600228229401496703205376
GHC parancsok lehet rövidített formában is használni õket, a parancs kezdõbetûjével: :load fnev.hs az fnev.hs nevû állomány betöltése, :reload fnev.hs az fnev.hs nevû állomány újrabetöltése, :type kif a kif kifejezése típusának a lekérdezése, :t kif :? az összes GHC parancs lekérdezése, :quit kilépés a GHC-bõl, :set +t a kiértékelés után a kifejezés típusa is megjelenik :unset +t az elõzõ beállítás visszavonása :set +s a kiértékelés után megjelenik az eltelt idõ és a lefoglalt bájtok száma :!cd az aktuális könyvtár (directory) állapot lekérdezése :cd C:\Diak könyvtár (directory) változtatás...