Funkcionális programozás 1. előadás Sapientia Egyetem, Matematika-Informatika Tanszék Marosvásárhely, Románia mgyongyi@ms.sapientia.ro 2018, tavaszi félév
Követelmények, osztályozás Előadás, jelenlét: Az előadásokon való jelenlét 60%-ban kötelező, ellenkező esetben nem lehet részt venni az első vizsgaalkalmon. Vizsgajegy (kollokvium): (írásbeli jegy + laborjegy)/2, ahol mindkét jegy el kell érje az 5-öst. Írásbeli jegy: 9 pontos írásbeli tételsorból áll és az utolsó előadáson kerül rá sor, vagy projekt készítés: témaválasztás (5-6 héten), projekt-vázlat leadás (7. héten, 1 A4 oldal), projekt-leadás (szoftver + dokumentáció, a 11. héten) projekt-bemutatás (15 perces előadás, 12-13 héten) Laborjegy: a gyakorlatvezető adja.
Könyvészet Simon Thompson, Haskell, The Craft of Functional Programming, Addison-Wesley. Graham Hutton, Programming in Haskell, Cambridge University Press, 2007. Horváth Zoltán, Programnyelvek/A funkcionális programozási nyelvek elemei. http://learnyouahaskell.com/ http://learn.hfm.io/ Prognyelvek portál: http://nyelvek.inf.elte.hu/leirasok/haskell/index.php
Programozási módszerek: imperatív programozás programozási nyelvek: assembly, Java, C, C++, stb., alapvezérlési szerkezete: a feltételes utasítás, a ciklus utasítás jellemzők: felszóĺıtó mód: parancsok, utasítások sorozata, amelyek tetszőleges módon változtathatják az eltárolt adat (változó) értékét, a lényeg az algoritmus megtalálása (hogyan oldjuk meg a feladatot) pontosan követhetőek a végrehajtott lépések.
Programozási módszerek: funkcionális programozás programozási nyelvek: Lisp, Haskell, Clean, alapeszköze: a függvénykiértékelés, a rekurzív függvényhívás, a változó: nincs előző értéket megsemmisítő értékadásra lehetőség, a programkód sokkal tömör, rövid, kezdete az 1930-as években megjelenő lambda kalkulusra vezethető vissza, funkcionális programozási stílusra imperatív nyelvek esetében is lehetőség van.
Programozási módszerek: logikai programozás programozási nyelvek: Prolog, SQL alapeszköze: a reláció-tények, szabályok megadása, ezek rekurzív alkalmazása. gyökerei hasonlóan a lambda kalkulusban találhatóak, legfontosabb alkalmazási területe a mesterséges intelligencia.
Példaprogram A faktoriáls függvény C nyelvben megírt változtai: 1. változat: int faktorialis1( int n ) { int i, res; if ( n<0 ) return -1; if ( n==0 ) return 1; for( i=1, res=1; i<=n; i++) res *= i; return res; } 2. változat: int faktorialis2( int n ) { if ( n<0 ) return -1; if ( n==0 ) return 1; else return n * faktorialis2(n-1); } 3. változat: int faktorialis3( int res, int n) { if ( n<0 ) return -1; if ( n==0 ) return res; return faktorialis3(n*res, n-1); } a függvényhívások: X = faktorialis1 (10); X = faktorialis2 (10); X = faktorialis3 (1, 10);
Példaprogram A faktoriáls függvény Haskell nyelvben megírt változtai: 1. változat: faktorialis1 :: Int -> Int faktorialis1 0 = 1 faktorialis1 n = n * faktorialis1 (n-1) a függvényhívások: Prelude> faktorialis1 10 Prelude> faktorialis2 10 Prelude> faktorialis3 1 10 2. változat: faktorialis2 :: Int -> Int faktorialis2 n n < 0 = -1 n == 0 = 1 otherwise = n * faktorialis2 (n-1) 3. változat: faktorialis3 :: Int -> Int -> Int faktorialis3 res n n < 0 = -1 n == 0 = res otherwise = faktorialis3 (n*res) (n-1)
Példaprogram A faktoriáls függvény Prolog nyelvben megírt változata: faktorialis(0, 1). faktorialis(n, Res) :- N >0, N1 is N - 1, faktorialis(n1, Res1), Res is N * Res1. a formula kiértékelése: faktorialis(10, X).
Haskell, történelmi háttér 1930-ban Alonzo Church kifejlesztette a lambda kalkulust, matematikai függvények vizsgálata céljából, 1950-ben megjelenik a LISP (LISt Processor), az első funkcionális programozási nyelv, 1970-ben megjelenik az FP (Functional Programming), amely először vezeti be a magasabb rendű függvényeket, 1987-ben egy nemzetközi bizottság egy új, lusta kiértékelési stratégiával működő funkcionális programozási nyelv fejlesztése mellett dönt, a nyelv neve Haskell lesz, Haskell Curry (amerikai matematikus) neve után, 2003-ban jelenik meg az első igazán megbízható Haskell verzió.
A Haskell programozási nyelv több implementációja is ismert: Hugs, amely egy interpreter, leginkább oktatásban használják GHC (Glasgow Haskell Compiler), valós alkalmazások fejlesztéséhez használják, natív kódra kompilál, biztosítja a párhuzamos végrehajtást, a debuggolást, a hatékonyság elemzését a GHC komponensei: ghc: a kompilátor, ami a natív kódot generálja ghci: az interaktív interpreter és debugger runghc: a Haskell programokat futtatja, mint szkripteket, anélkül hogy előbb kompilálni kellene őket
A Haskell programozási nyelv https://www.haskell.org/platform/ Windows alatt: GHCi, WinGHCi, ezek futtatásakor egy képernyőn megjelenik a Prelude> prompt a Prelude> prompt jelzi hogy sikeresen betöltődött a standard könyvtár, utána parancsok, kifejezések stb. adhatóak meg további könyvtárcsomagok tölthetőek itt be, a prompt neve megváltoztatható használható számológépként szövegszerkesztővel hs kiterjesztésű állományokat írhatunk, alkalmazásokat fejleszthetünk, ezek kompilálhatóak, futtathatóak Haskell alapfogalmak: értékek, függvények, típusok
Haskell, mint számológép Prelude> 10 + 3 13 Prelude> 10 / 3 3.3333333333333335 Prelude> 10 div 3 3 Prelude> 10 ** 3 1000.0 Prelude> 7.5 * 4.3 32.25 Prelude> div 10 3 3 Prelude> 10 ^ 3 1000 Prelude> 2 ** 0.5 1.4142135623730951 Prelude> 2 ^ 0.5 <interactive>:67:3: Could not deduce (Integral b0) arising from a use of ^
Haskell, mint számológép > "Hello " ++ "vilag" ++ "!" "Hello vilag!" > length("hello vilag!") 12 > sum [1, 2, 3, 4, 5] 15 > sum [1..10] 55 > madarak = ["rigo", "cinke", "harkaly"] madarak :: [[Char]] > "pinty" : madarak ["pinty", "rigo", "cinke", "harkaly"] > ("Hello " ++ "vilag!") == "Hello vilag!" True > import Data.List > sort [1, 6, 5, -10, 7] [-10, 1, 5, 6, 7]
Haskell, az első lépések A Haskell prompt után függvényt is definiálhatunk, majd meghívhatjuk: > let terulet r = if r < 0 then error "Rossz benmenet!" else r * r * pi > terulet 5 78.53981633974483 a függvényt egy szövegszerkesztőben (pl. Notepad++) is definiálhatjuk, létrehozva egy állományt, ennek a kiterjesztése hs kell legyen a WinGHCi által alapból felkínált szövegszerkesztő a Notepad, ez elérhető menüből: Actions/Open Text Editor
Haskell, az első program Legyen az elso.hs tartalma a következő, ahol el kell hagyni a let kifejezést: terulet :: Double -> Double terulet r = if r < 0 then error "Rossz benmenet!" else r * r * pi Első lépésben értelmezzük az elso.hs-t: > :load "elso.hs" Windows alatt, a WinGHCi-ben ez elérhető a File/Load menüpontból is, a megfelelő állomány kiválasztásával ezután kiértékelhetjük: > terulet 10
Haskell, az első program Ha módosítjuk az elso.hs tartalmát a következőképpen, akkor futtatható állomány hozható létre: terulet :: Double -> Double terulet r = if r < 0 then error "Rossz benmenet!" else r * r * pi main = print (terulet 10) ehhez WinGHCi-ben előbb kiválasztjuk, betöltjük a File/Load menüponttal a megfelelő állományt, majd az exe létrehozása a Tools/GHC compiler menüpont aktiválásával jön létre, a prompt használatával: :! ghc --make "elso.hs" -o main.exe a futtatás: > main 314.1592653589793 megválaszthatjuk a futtatható állomány nevét is: :! ghc --make "elso.hs" -o elso.exe
Alaptípusok Bool: logikai típus, két értékkel: False, True, Char: egyetlen (Unicode) karakter eltárolására alkalmas, időzőjel közé kell írni az értéket: A, \n, +, String: karakterláncok kezelésére alkalmas, macskaköröm közé kell írni az értékeket: "Hello Haskell", "10 * 0 = 0", Int: rögzített pontosságú egész számok, 2 31 közötti 2 31 értékek kezelésére alkalmas, Integer: tetszőleges pontosságú egész számok kezelésére alkalmas. Az Int és Integer közötti választást elsősorban a hatékonyság alapján kell eldönteni, Float, Double: valós számok kezelésére alkalmas. A Double 64-bites lebegőpontos ábrázolású, használata ajánlott, ellentétben a Float-tal, aminek használata kevésbé ajánlott.
Osztálytípusok Egy függvény argumentumai nem csak egy adott típushoz tartozó értékek lehetnek. A Haskell bevezeti a típusváltozó fogalmát, amit azt jelenti hogy a függvényargumentumok specifikációjakor típusosztályokat is megadhatunk. Jelölésükre az angol ábécé kis betűit használjuk. A terulet függvény szignatúráját a következőképpen módosíthatjuk: terulet :: (Ord t, Floating t) => t -> t terulet r = if r < 0 then error "Rossz benmenet!" else r * r * pi Ha a parancssorban, a korábban használt sort függvény szignatúráját lekérdezzük a :t (type) paranccsal, akkor a következő választ kapjuk: > :t sort sort :: Ord a => [a] -> [a] Ez azt jelenti, hogy a sort függvény minden olyan típusú adatra meghívható, amely az Ord osztálytípusba tartozik. Például egész számok rendezése mellett karakterláncokat is rendezhetünk: > sort ["pinty", "rigo", "cinke", "harkaly"]
Típusosztályok az Eq azokat a típusokat tartalmazza, amelyek az egyenlőség és nem egyenlőség operátorokkal összehasonĺıthatóak (==) :: a -> a -> Bool (/=) :: a -> a -> Bool > "hello" /= "Hello" True > 13.5 == 3.4 False
Típusosztályok az Ord azokat a típusokat tartalmazza, amelyek sorba rendezhetőek (<) :: a -> a -> Bool (<=) :: a -> a -> Bool (>) :: a -> a -> Bool (>=) :: a -> a -> Bool min :: a -> a -> Bool max :: a -> a -> Bool compare :: Ord a => a -> a -> Ordering > compare 4 5 LT > compare "hello" "hello" EQ > compare a Z GT
Típusosztályok a Num azokat a típusokat tartalmazza, amelyeknek numerikus értékük van, (+) :: a -> a -> a (-) :: a -> a -> a (*) :: a -> a -> a negate :: a -> a abs :: a -> a signum :: a -> a nem rendelkezik az osztási operátorral
Típusosztályok az Integral magába foglalja a Num osztályokba tartozó típusokat és azokat a típusokat is amelyeken egész osztás és maradékos osztás végezhető (div) :: a -> a -> a (mod) :: a -> a -> a (quot) :: a -> a -> a (rem) :: a -> a -> a > mod (-3) 4 1 > rem(-3) 4-3
Típusosztályok a Fractional magába foglalja a Num osztályokba tartozó típusokat és azokat a típusokat is amelyeken valós osztás és reciprok érték határozható meg (/) :: a -> a -> a (recip) :: a -> a > recip 2.0 0.5
Típusosztályok a Floating magába foglalja a valós számokat kezelő, azaz a Float és Double típusokat. pi :: Floating a => a (**) :: Floating a => a -> a -> a sqrt, exp, log :: Floating a => a -> a sin, cos, tan :: Floating a => a -> a.
Típusosztályok a Show azokat a típusokat tartalmazza, amelyek értékei átalakíthatóak karakterlánccá (String-é). magába foglalja az alaptípusokat (Bool, Char, String, Int, Integer, Float, Double, Lista, Tuple): show :: a -> String > show 579 "579" > show ( b, True) "( b, True)"
Típusosztályok a Read azokat a típusokat tartalmazza, amelyek értékét meg lehet határozni karakterláncból, magába foglalja az alaptípusokat (Bool, Char, String, Int, Integer, Float, Double, Lista, Tuple): read :: String -> a > read "False" :: Bool False > read "[10, 11, 12, 13]" :: [Int] [10, 11, 12, 13]
A standard Haskell osztálydiagramm