Funkcionális programozás 7. 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ó? ö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
Miről lesz szó? Haskell I/O műveletek, feladatok írás állományba, formázva (Hamming számok) állomány nagybetűsítése bináris állomány hexa alakja állomány mérte, bájtban állományról való másolatkészítés két állomány tartalmának összehasonĺıtása
Haskell I/O műveletek, állománykezelés 1. feladat Generáljuk ki az első n Hamming számot növekvő sorrendbe és írjuk ki egy állományba (hamming.txt) őket, minden sorba kíırva a szám sorszámát, majd space-el elválasztva a Hamming számot. import System.IO hammingir :: IO() hammingir = do putstr "n = " temp <- getline let n = read temp :: Int let lhamming = take n hammingf outf <- openfile "hamming.txt" WriteMode fokiir outf lhamming 1 hclose outf
Haskell I/O műveletek, állománykezelés fokiir :: Handle -> [Integer] -> Integer -> IO() fokiir outf [] _ = return () fokiir outf (k: ve) i = do hputstrln outf $ (show i) ++ " " ++ (show k) fokiir outf ve (i + 1)
Hamming számok egy Hamming szám általános alakja: 2 n 3 n Az első 10 Hamming szám: [ 1,2,3,4,6,8,9,12,16,18 ] Az algoritmus elvi működése: Az első Hamming szám az 1. Meghatározzuk azt a két listát, melynek elemei a 2 h, illetve 3 h összefüggéssel határozhatóak meg, ahol h egy tetszőleges Hamming szám. Az így kigenerált két listát összefésüljük.
Hamming számok A listák meghatározása: hammingf :: [Integer] hammingf = (1: listah) where haml = hammingf lista2h = [ 2 * h h <- haml ] lista3h = [ 3 * h h <- haml ] listah = mymerge lista2h lista3h
Hamming számok A kigenerált két lista összefésülése mymerge :: [Integer] -> [Integer] -> [Integer] mymerge [] [] = [] mymerge ls [] = ls mymerge [] ls = ls mymerge ls1 ls2 k1 < k2 = (k1: mymerge ve1 ls2) k1 == k2 = mymerge ls1 ve2 k1 > k2 = (k2: mymerge ls1 ve2) where k1 = head ls1 k2 = head ls2 ve1 = tail ls1 ve2 = tail ls2
Haskell I/O műveletek, állománykezelés 2. feladat Olvassuk és írjuk ki a hamming.txt állomány tartalmát hammingolvas1 = do inf <- openfile "hamming.txt" ReadMode fobeolvas1 inf hclose inf fobeolvas1 inf = do heof <- hiseof inf if heof then return () else do temp <- hgetline inf putstrln temp fobeolvas1 inf
Haskell I/O műveletek, állománykezelés 3. feladat Olvassuk ki a hamming.txt állomány tartalmát, és tegyük át listába a Hamming számokat. hammingolvas2 = do inf <- openfile "hamming.txt" ReadMode hlista <- fobeolvas2 inf putstrln $ show hlista hclose inf fobeolvas2 inf = do heof <- hiseof inf if heof then return [] else do temp <- hgetline inf let [i, k] = map (read :: String -> Integer ) $ words temp ve <- fobeolvas2 inf return (k: ve)
Haskell I/O, fontosabb függvények A paraméterként megadott string kíıratása az állományba: hputstr :: Handle -> String -> IO () Hasonló a hputstr-hez, csak újsort tesz a kíırás végére: hputstrln :: Handle -> String -> IO () A megadott adat string alakját, amelyet a show szolgáltat kíırja az állományba: hprint :: Show a => Handle -> a -> IO ()
Haskell I/O, fontosabb függvények A paraméterként megadott állomány megnyitása. Megnyítási módok: ReadMode, WriteMode, AppendMode openfile :: FilePath -> IOMode -> IO Handle A paraméterként megadott állomány bezárása: hclose :: Handle -> IO () True-t térít vissza ha az állomány végén vagyunk, másképp False-t: hiseof :: Handle -> IO Bool A visszatérített stringen keresztül elérhetjük az állományban levő összes karaktert, feldolgozási módjuk azonban lusta, ezért csak annyi karakter kerül kiolvasásra amennyit éppen feldolgoz egy adott függvény: hgetcontents:: Handle -> IO String
Haskell I/O, fontosabb függvények A paraméterként megadott állomány teljes tartalmának lusta módon való feldolgozása, az állomány tartalma egy stringbe kerül: readfile :: FilePath -> IO String A string tartalmát kíırja a paraméterként megadott állományba: writefile:: FilePath -> String -> IO () A string tartalmát, a paraméterként megadott állományhoz fűzi: appendfile :: FilePath -> String -> IO () Hasonló módon dolgozza fel az állományt mint a readfile, writefile, csak a be illetve kimenet a standard input, illetve output: interact:: (String -> String) -> IO () Bináris állomány megnyitása: openbinaryfile :: FilePath -> IOMode -> IO Handle
Haskell I/O műveletek, állománykezelés 4. feladat Nagybetűsítsük az input.txt tartalmát, az eredmény az output.txt állományba kerül, I. módszer. import Data.Char mainnagybetusit :: IO () mainnagybetusit = do inf <- readfile "input.txt" writefile "output.txt" $ map toupper inf
Haskell I/O műveletek, állománykezelés 5. feladat Nagybetűsítsük az input.txt tartalmát, az eredmény az output.txt állományba kerül, II. módszer. import Data.Char import System.IO mainnagybetusit2 :: IO() mainnagybetusit2 = do inf <- openfile "input.txt" ReadMode outf <- openfile "output.txt" WriteMode fofeldolgoz inf outf hclose inf hclose outf
Haskell I/O műveletek, állománykezelés Az állomány tartalmát soronként (adatonként) dolgozzuk fel. fofeldolgoz :: Handle -> Handle -> IO() fofeldolgoz inf outf = do feof <- hiseof inf if feof then return () else do temp <- hgetline inf hputstrln outf (map toupper temp) fofeldolgoz inf outf
Haskell I/O, állománykezelés 6. feladat Írjuk ki egy szövegállományba egy tetszőleges állomány bájtjainak, hexa értékét. import Data.Char import System.IO mainhexa :: IO () mainhexa = do infb <- openbinaryfile "file.pdf" ReadMode outt <- openfile "filehexa.txt" WriteMode bstr <- hgetcontents infb let bhexa = alakithexa bstr hputstr outt bhexa hclose infb hclose outt
Haskell I/O, állománykezelés Az alakithexa forráskódja, I módszer, ahol a convfromnr a 6. előadásból vett függvény. import Eload6 alakithexa :: [Char] -> [Char] alakithexa (k: ve) = if null ve then tempk else newk ++ alakithexa ve where tempk = map inttodigit $ reverse $ convfromnr (ord k) 16 newk = tempk ++ " " > inttodigit 15 f -- a bemeneti egész számot, amely kisebb kell legyen mint 16 -- átalakítja hex szimbólummá > digittoint a 10
Haskell I/O, állománykezelés Az alakithexa forráskódja, II módszer import Numeric alakithexa2 :: [Char] -> [Char] alakithexa2 (k: ve) = if null ve then tempk else newk ++ alakithexa1 ve where tempk = showhex (ord k) "" newk = tempk ++ " " Példák a showhex használatára: > showhex 1024 "" "400" > showhex 10 "" "a"
Haskell I/O, bináris állományok 7. feladat Határozzuk meg egy állomány bájt-méretét. mainallmeret :: IO () mainallmeret = do inf <- openbinaryfile "kep.tif" ReadMode size <- hfilesize inf putstrln "fsize: " putstrln (show (fromintegral size)) hclose inf A hfilesize az állomány bájt-méretét adja meg, szignatúrája: hfilesize :: Handle -> IO Integer
Haskell I/O, bináris állományok 8. feladat Készítsünk másolatot egy tetszőleges állományról, I. módszer. mainmasoli :: IO () mainmasoli = do inf <- openbinaryfile "kep.jpg" ReadMode outf <- openbinaryfile "nkep.jpg" WriteMode blista <- beolvasbajtok inf kiirbajtok outf blista hclose inf hclose outf
Haskell I/O, bináris állományok Az író/olvasó segédfüggvények: beolvasbajtok :: Handle -> IO [Char] beolvasbajtok inf = do heof <- hiseof inf if heof then return [] else do nr <- hgetchar inf nlista <- beolvasbajtok inf return (nr: nlista) kiirbajtok :: Handle -> [Char] -> IO () kiirbajtok out [] = return () kiirbajtok outf (k: ve) = do hputchar outf k kiirbajtok outf ve
Haskell I/O, bináris állományok 9. feladat Készítsünk másolatot egy tetszőleges állományról, II. módszer. Ez a lusta kiértékelési stratégia miatt gyorsabb lesz. mainmasolii :: IO () mainmasolii = do inf <- openbinaryfile "kep.jpg" ReadMode outf <- openbinaryfile "nkep.jpg" WriteMode blista <- hgetcontents inf hputstr outf blista hclose inf hclose outf
Haskell I/O, bináris állományok 10. feladat Hasonĺıtsuk össze két állomány tartalmát. mainhasonliti :: IO () mainhasonliti = do inf1 <- openbinaryfile "kep.jpg" ReadMode inf2 <- openbinaryfile "nkep.jpg" ReadMode bind <- hasonlitbajtok inf1 inf2 0 0 if bind == 0 then putstrln "OK, identical files" else do putstr "different files on poz: " putstrln $ show bind hclose inf1 hclose inf2
Haskell I/O, bináris állományok Az alkalmazott segédfüggvény: hasonlitbajtok :: Num a => Handle -> Handle -> a -> a -> IO a hasonlitbajtok inf1 inf2 bind bind1 = do heof1 <- hiseof inf1 heof2 <- hiseof inf2 if heof1 && heof2 then return bind else do if heof1 heof2 then return bind1 else do nr1 <- hgetchar inf1 nr2 <- hgetchar inf2 if nr1 /= nr2 then return bind1 else hasonlitbajtok inf1 inf2 bind (bind1 + 1) A hasonlitbajtok függvény nem minden esetben működik helyesen, melyik/melyek ez az eset? Javítsuk ki!!!
Haskell I/O, bináris állományok 11. feladat Hasonĺıtsuk össze két állomány tartalmát; gyorsabb lesz, mint az előző, a lusta kiértékelési stratégia miatt. mainhasonlitii :: IO () mainhasonlitii = do inf1 <- openbinaryfile "kep.jpg" ReadMode inf2 <- openbinaryfile "nkep.jpg" ReadMode bstr1 <- hgetcontents inf1 bstr2 <- hgetcontents inf2 let bind = hasonlitbajtokii bstr1 bstr2 0 if bind == 0 then putstrln "OK, identical files" else do putstr "different files on poz: " putstrln $ show bind hclose inf1 hclose inf2
Haskell I/O, bináris állományok Az alkalmazott segédfüggvény:: hasonlitbajtokii :: (Eq a1, Num a) => [a1] -> [a1] -> a -> a hasonlitbajtokii [] [] bind = 0 hasonlitbajtokii [] ve bind = bind hasonlitbajtokii ve [] bind = bind hasonlitbajtokii (k1: ve1) (k2: ve2) bind (k1 /= k2) = bind otherwise = hasonlitbajtokii ve1 ve2 (bind + 1) A hasonlitbajtokii függvény nem minden esetben működik helyesen, melyik/melyek ez az eset? Javítsuk ki!!!