Funkcionális programozás 6. előadás Sapientia Egyetem, Matematika-Informatika Tanszék Marosvásárhely, Románia mgyongyi@ms.sapientia.ro 2018, tavaszi félév
Miről volt szó? Haskell modulok, kompilálás a Show és Read típusosztályok explicit típuskonverziók a struct típus rendezések: beszúrásos rendezés (insert sort), gyorsrendezés (quick sort)
Miről lesz szó? összefésüléses rendezés (merge sort) Haskell I/O műveletek feladatok: számrendszerek közötti átalakítások Eratosztenész szitája
Összefésüléses rendezés Két rendezett lista összefésülése merge :: (Ord a) => [a] -> [a] -> [a] merge [] [] = [] merge [] ls = ls merge ls [] = ls merge (k1: ve1) (k2: ve2) k1 < k2 = (k1: merge ve1 (k2: ve2)) otherwise = (k2: merge (k1: ve1) ve2) > merge [4,7,9,10] [1,2,8] [1,2,4,7,8,9,10]
Összefésüléses rendezés A merges a merge függvény segítségével rendezi a paraméterként megadott lista elemeit. mergesort :: (Ord a) => [a] -> [a] mergesort [] = [] mergesort [k] = [k] mergesort ls = merge blista jlista where db = div (length ls) 2 blista = mergesort (take db ls) jlista = mergesort (drop db ls) > merges [3,12,6,7,5,9,10,2,10,1] [1,2,3,5,6,7,9,10,10,12]
Haskell I/O műveletek, példa Az elsoio függvény egy "Hello vilag" üzenetet ír ki, a masodikio segítségével pedig egy párbeszédet szimulálunk: elsoio :: IO () elsoio = do putstrln "Hello vilag" masodikio :: IO () masodikio = do putstr "Mi a neved? " str1 <- getline putstrln ("Hello " ++ str1) putstr "Hany eves vagy? " str2 <- getline putstrln $ "Eveid szama: " ++ str2
Haskell I/O műveletek, példa 1. feladat Író/olvasó függvények segítségével határozzuk meg x a értékét: mainhatv :: IO () mainhatv = do putstr "Alap: " temp <- getline let x = read temp :: Integer putstr "Kitevo: " temp <- getline let a = read temp :: Integer putstr "Eredmeny: " let res = x ^ a putstrln $ show res > mainhatv Alap: 2 Kitevo: 100 Eredmeny: 1267650600228229401496703205376
Haskell I/O műveletek, példa 2. feladat Író/olvasó függvények segítségével határozzuk meg az n szám páros osztóinak listáját, ahol halmazműveleteket használunk a kért lista előálĺıtásához: mainparoso :: IO () mainparoso = do putstr "n = " temp <- getline let n = read temp :: Int let lista = [i i <- [2, 4.. n], mod n i == 0] putstrln $ show lista > mainparoso n: 60 [2,4,6,10,12,20,30,60]
Haskell I/O műveletek, programozási stílus A Haskell a tiszta funkcionális stílusban írt programrészt elválasztja attól a programrésztől, ahol I/O műveleteket végez. Az I/O műveletek megadása hasonló az imperatív stílusú programrészekhez. A Haskell támogatja az imperatív stílust, amikor I/O műveletek definiál vagy tömböket használ. A Haskell két szintre osztja a programkódot: kifejezések kiértékelése, akciók végrehajtása. A kifejezések kiértékelésekor nem történik végrehajtás, nem léphetnek fel mellékhatások, ez a tulajdonképpeni tiszta funkcionális stílusban írt programrészek megadását jelentik. I/O műveletek végzésekor, azaz akciók végrehajtásakor előállhatnak mellékhatások, ez az imperatív stílusú programrészek megadását jelentik.
Haskell I/O műveletek, programozási sítlus Az akciók típusa: I/O a, ahol a tetszőleges típus, az akció végrehajtásakor kap értéket. Van olyan akció, amelyiknek nem lényeges az eredménye, ez a nullás akció, jele: (). Leginkább akkor van szerepe, amikor I/O műveletek során nem a visszatrérítési értkék a fontos, hanem a végrehajtott akció. a do kifejezés lehetővé teszi az akciók egymás után való fűzését. minden sorába egy-egy akciót írhatunk, típusa megegyezik az utolsó sorban levő akció típusával.
Haskell I/O műveletek, programozási sítlus putstr :: String -> IO () szöveg kíıratása, a visszatérítési érték nem szignifikáns, ezért a kimenet típusa IO(). getline :: IO String szöveg beolvasása enterig, a visszatérítési értéke egy IO String. getchar :: let IO Char egyetlen karakter beolvasása, a visszatérítési értéke egy IO Char. do blokkban használjuk lokális definíciók megadására.
Haskell I/O műveletek, feladatok 3. feladat Írjunk függvényt, amely beolvas egy egész számot a billentyűzetről. olvasint :: IO Int olvasint = do str <- getline return (read str :: Int) 4. feladat Írjunk függvényt, amely beolvas egy valós számot a billentyűzetről. olvasdouble :: IO Double olvasdouble = do str <- getline return (read str :: Double)
Haskell I/O műveletek, feladatok 5. feladat Olvassunk be több egész számot a billentyűzetről és tegyük őket egy listába. I. megoldás: az eredménylista első eleme az elsőként beolvasott szám lesz olvasnszam :: Int -> IO [Int] olvasnszam n = do k <- olvasint if n == 1 then return [k] else do ve <- olvasnszam (n-1) return (k: ve)
Haskell I/O műveletek, feladatok 6. feladat Olvassunk be több valós számot a billentyűzetről és tegyük őket egy listába. II. megoldás: az eredménylista első eleme az utolsóként beolvasott szám lesz egy egész szám beolvasása: olvasnszamf :: Int -> IO [Double] olvasnszamf n = solvas n [] where solvas n res = do k <- olvasdouble if n == 1 then return (k: res) else solvas (n-1) (k: res)
Haskell I/O műveletek, feladatok 7. feladat Olvassunk be több egész számot a billentyűzetről és tegyük őket egy listába. III. megoldás: üres sor beolvasásáig végezzük a számok beolvasását. olvasszamv :: IO[Int] olvasszamv = do temp <- getline if temp == [] then return [] else do let k = read temp :: Int ve <- olvasszamv return (k: ve)
Haskell I/O műveletek, feladatok 8. feladat Olvassunk be több egész számot a billentyűzetről és tegyük őket egy listába. IV. megoldás: egy sorba több számot olvasok be space-ekkel elválasztva olvassor :: IO [Integer] olvassor = do temp <- getline let lista = map (read :: String -> Integer) $ words temp return lista > words "3094 156 2101 201 89" ["3094","156","2101","201","89"] > olvassor 3094 156 2101 201 89 [3094,156,2101,201,89]
Haskell I/O műveletek, feladatok 9. feladat Olvassuk be egy szám számjegyeit és a megfelelő számrendszert; alakítsuk át a számot 10-es számrendszerbe, majd ellenőrizzük az eredményt. A convtonr és convfromnr függvényeket a következő oldalakon találjuk. mainconv = do putstr "number s digits: " ls <- olvassor putstr "base: " str <- getline let b = read str :: Integer let nr = convtonr ls b putstr "Result: " putstrln (show nr) let ls = convfromnr nr b putstr "Check result: " putstrln (show ls)
Feladatok 10. feladat Határozzuk meg egy szám tetszőleges számrendszerbeli (fordított sorrend) alakja alapján a szám 10-e számrendszerbeli alakját convtonr :: (Num a) => [a] -> a -> a convtonr ls b = sconvtonr ls b 1 where sconvtonr [] b pow = 0 sconvtonr (k: ve) b pow = k * pow + sconvtonr ve b (pow * b) > convtonr [0,0,0,0,0,0,0,0,1] 2 256 > convtonr (convfromnr 1234 16) 16 1234
Feladatok 11. feladat Határozzuk meg egy szám tetszőleges számrendszerbeli alakját, a fordított sorrendet (sok esetben erre van szükség). convfromnr :: (Num a, Integral a) => a -> a -> [a] convfromnr nr b nr < b = [nr] otherwise = (nr mod b): (convfromnr (nr div b) b) > convfromnr 256 2 [0,0,0,0,0,0,0,0,1] > convfromnr 2048 256 [0,8]
Haskell I/O műveletek, feladatok A 9. feladat meghívása: > mainconv number s digits: 23 4 5 78 12 11 base: 100 Result: 111278050423 Check result: [23,4,5,78,12,11] Megjegyzés: 111278050423 = 23 + 4 100 + 5 100 2 + 78 100 3 + 12 100 4 + 11 100 5
Haskell I/O műveletek, állománykezelés 12. feladat Eratoszthenész szitájával generáljuk ki az első n prímszámot és az eredményt írjuk ki egy állományba (prim.txt). eszita :: Int -> [Int] eszita n = take n (2: szita [3, 5..]) szita :: [Int] -> [Int] szita (k: ve) = (k: szita [x x <- ve, mod x k /= 0]) primfileir :: IO () primfileir = do putstr "n = " temp <- getline let n = read temp :: Int let lprim = eszita n writefile "prim.txt" (show lprim)
Haskell I/O műveletek, állománykezelés 13. feladat Olvassuk ki és írjuk ki a képernyőre a prim.txt állomány tartalmát. primfileolvas :: IO() primfileolvas = do inf <- readfile "prim.txt" let lprim = read inf :: [Int] putstrln $ show lprim let nr = length lprim putstr "primek szama: " putstrln $ show nr A readfile, writefile megoldják az állományok megnyítását, bezárását, az adatolvasást, az adatkíırást.
Haskell I/O műveletek, állománykezelés 14. feladat Olvassuk ki a szamok.txt állomány tartalmát, rendezzük és a rendezett elemeket írjuk ki az rszamok.txt állományba. rendezfile = do inf <- readfile "szamok.txt" let lszamok = read inf :: [Int] let rszamok = mergesort lszamok writefile "rszamok.txt" $ show rszamok A kiolvasáskor feltételeztük, hogy a szamok.txt tartalma a következő struktúrájú [10,23,11,5,101,85,64,24] Azaz a számok szögletes zárójel között vannak, és elválasztójelnek a vesszőt használtuk. Más struktúra esetén módosul a kiolvasási algoritmus.