Funkcionális programozás 10. 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ó? a foldl és foldr függvények lista első, utolsó eleme lista hossza listaelemek összege egy eleme szerepel-e a listaelemek között kombinatorikai feladatok m elemű kombinációk meghatározása részhalmazok előálĺıtása N királynő feladat Pascal féle háromszög
Miről lesz szó? szekvenciális keresés: szövegállományban, bináris állományban bináris keresés, tömbök használata jelszavak hash értékének meghatározása
Szekvenciális keresés 1. feladat Olvassunk be egy szót a billentyűzetről és vizsgáljuk meg, hogy az szerepel-e egy adott szövegállományban. mainf1 :: IO() mainf1 = do putstr "szo = " szo <- getline inf <- openfile "szavak.txt" ReadMode feldolgoz1 inf szo 1 hclose inf keresf1 :: (Num a, Eq a1) => [a1] -> a1 -> a -> a keresf1 [] szo ind = 0 keresf1 (k: ve) szo ind k == szo = ind otherwise = keresf1 ve szo (ind + 1)
Szekvenciális keresés Az alkalmazott segédfüggvények: feldolgoz1 :: (Show a, Num a) => Handle -> String -> a -> IO () feldolgoz1 inf szo ind = do feof <- hiseof inf if feof then return () else do lista <- hgetline inf let res = keresf1 (words lista) szo 1 if res /= 0 then do putstr "sorszam: " print ind putstr "oszlopszam: " print res return () else feldolgoz1 inf szo (ind + 1)
Szekvenciális keresés, bináris állományban 2. feladat Vizsgáljuk meg, hogy egy adott bájtszekvencia benne van-e egy bináris állományban. A bájtszekvencia beolvasása hexa stringként történik, pl: 6F 9A 44 BD E7 mainf2 :: IO () mainf2 = do putstr "all nev: " nev <- getline putstr "Hexa bajt str: " temp <- getline let bmit = map alakith_b (words temp) inf <- openbinaryfile nev ReadMode bmiben <- hgetcontents inf let res = keresf2 bmiben bmit putstr "eredmeny: " putstrln $ show res hclose inf
Szekvenciális keresés, bináris állományban A alakith_b az egy vagy két szimbólumból álló hexa értékből előálĺıtja a megfelelő bájtot. alakith_b :: [Char] -> Char alakith_b [c] = chr (hexkar c) alakith_b [c1, c2] = chr ((hexkar c1) * 16 + (hexkar c2)) alakith_b _ = error "Hiba" import Data.Char alakith_b1 :: [Char] -> Char alakith_b1 [c] = chr (digittoint c) alakith_b1 [c1, c2] = chr ((digittoint c1) * 16 + (digittoint c2)) alakith_b1 _ = error "Hiba"
Szekvenciális keresés, bináris állományban A hexkar függvény egy hexa ( 0, 1... F ) szimbólumnak meghatározza a megfelelő int típusú értékét. hexkar :: Char -> Int hexkar c c <= 9 && c >= 0 = (ord c) - 48 c == a c == A = 10 c == b c == B = 11 c == c c == C = 12 c == d c == D = 13 c == e c == E = 14 c == f c == F = 15 otherwise = error "Hiba"
Szekvenciális keresés, bináris állományban A keresf2 megkeresi hogy az ls bájtszekvenciában megtalálható-e a bs szekvencia. keresf2 :: [Char] -> [Char] -> Bool keresf2 [] bs = False keresf2 ls bs skeres ls bs = True otherwise = keresf2 (tail ls) bs where skeres [] ue = False skeres _ [] = True skeres (k: ve) (l: ue) k /= l = False otherwise = skeres ve ue
Szekvenciális keresés, struct típusú adathalmazban 3. feladat Egy szövegállományban egy adott személyről a következő adatok vannak eltárolva: vezetéknév, keresztnév, születési dátum. Hozzuk létre a következő típusú adatszerkezeteket, majd olvassuk ki az adatokat az állományból és határozzuk meg, egy adott dátumon született személyek listáját. A keresett születési dátumot a billentyűzetről olvassuk be. data Datum = Datum{ nap :: Int, honap:: Int, ev :: Int } deriving (Show) data Szemely = Szemely { vnev :: [Char], knev :: [Char], szdatum :: Datum } deriving (Show)
Szekvenciális keresés, struct típusú adathalmazban mainf3 :: IO () mainf3 = do putstr "keresett datum: " dat <- getline let datum = formaz dat inf <- openfile "szemely.txt" ReadMode lista <- feldolgoz3 inf let res = keresf3 datum lista putstr (show res) hclose inf > mainf3 keresett datum: 13 6 1972 [Szemely {vnev = "Szep", knev = "Sandor", szdatum = Datum {nap = 13, honap = 6, ev = 1972}}, Szemely {vnev = "Balogh", knev = "Andras", szdatum = Datum {nap = 13, honap = 6, ev = 1972}}]
Szekvenciális keresés, struct típusú adathalmazban A szemely.txt szerkezete a következő: Nagy Ferenc 5 10 1989 Bandi Zoltan 6 9 1972 Joo Erika 30 5 1976 Szep Sandor 13 6 1972 Kiss Malvin 25 12 1973... formaz :: String -> Datum formaz str = elem where lstr = words str nap = read (lstr!! 0) :: Int honap = read (lstr!! 1) :: Int ev = read (lstr!! 2) :: Int elem = Datum nap honap ev
Szekvenciális keresés, struct típusú adathalmazban feldolgoz3 :: Handle -> IO [Szemely] feldolgoz3 inf = do eof <- hiseof inf if eof then return [] else do sor <- hgetline inf let lsor = words sor let vnev = lsor!! 0 let knev = lsor!! 1 let nap = (read (lsor!! 2) :: Int) let honap = (read (lsor!! 3) :: Int) let ev = (read (lsor!! 4) :: Int) let elemd = Datum nap honap ev let eleml = Szemely vnev knev elemd lista <- feldolgoz3 inf return (eleml : lista)
Szekvenciális keresés, struct típusú adathalmazban keresf3 :: Datum -> [Szemely] -> [Szemely] keresf3 _ [] = [] keresf3 datum (k: ve) vizsgal datum (szdatum k) = (k: keresf3 datum ve) otherwise = keresf3 datum ve vizsgal :: Datum -> Datum -> Bool vizsgal d1 d2 nap d1 == nap d2 && honap d1 == honap d2 && ev d1 == ev d2 = True otherwise = False
Bináris keresés lista adatszerkezettel, I. módszer 4. feladat Alkalmazzuk a bináris keresés algoritmusát egy lista típusú adatszerkezeten. mainf4 :: Int mainf4 = bkeres1 lista 21 0 (length lista) where lista = [1,5,7,9,10,11,21,21,22,27,34,55,67,90] bkeres1 :: Ord a => [a] -> a -> Int -> Int -> Int bkeres1 ls k l h h < l = -1 p > k = bkeres1 ls k l (i-1) p < k = bkeres1 ls k (i+1) h otherwise = i where i = l + ((h - l) div 2) p = ls!! i
Bináris keresés lista adatszerkezettel, II módszer mainf4_2 :: Maybe Int mainf4_2 = bkeres2 lista 100 where lista = [1,5,7,9,10,11,21,34,55,67,90] bkeres2 :: Ord a => [a] -> a -> Maybe a bkeres2 [] _ = Nothing bkeres2 ls k k < p = bkeres2 (take (i-1) ls) k k > p = bkeres2 (drop (i+1) ls) k otherwise = Just k where i = ((length ls) - 1) div 2 p = ls!! i
Bináris keresés tömb adatszerkezettel 5. feladat Alkalmazzuk a bináris keresés algoritmusát egy tömb típusú adatszerkezeten. import Data.Array mainf5 :: Maybe Integer mainf5 = bkerestomb tomb 21 where tomb = listarray (0, 10) [1,5,7,9,10,11,21,34,55,67,90] > mainf5 Just 6
Bináris keresés tömb adatszerkezettel bkerestomb :: (Ix i, Integral i, Ord e) => Array i e -> e -> Maybe i bkerestomb a x = bkerest a x (bounds a) bkerest :: (Ix a, Ord a1, Integral a) => Array a a1 -> a1 -> (a, a) -> Maybe a bkerest a x (l, h) h < l = Nothing x < aelem = bkerest a x (l, i - 1) x > aelem = bkerest a x (i + 1, h) otherwise = Just i where i = (l + h) div 2 aelem = a! i
Feladatok 6. feladat Írjunk egy alkalmazást, amely a lenti leírás alapján meghatározza a jelszavak hash-értékét. További útmutatás: Diviánszky Péter - Jelszavak Jelszavak hash-értékének a meghatározása két lépésben történik Első lépésben egy kulcsképző függvény segítségével meghatározunk egy kulcsértéket, ami tulajdonképpen a jelszó normalizálását jelenti, Második lépésben ennek a kulcsértéknek határozzuk meg a tulajdonképpeni hash-értéket. A gyakorlatban alkalmazott hash függvények: az SHA-függvénycsalád stb, A kulcsképző függvény lehet többféle, a feladatban két kulcsképző függvény kerül alkalmazásra, mindkét esetben egy toldalék, a salt alapján történik a kulcsérték meghatározása A hash-érték meghatározását is többféleképpen végezhetjük, a feladatban a következő egyszerű képlet kerül alkalmazásra: hash[c 1, c 2,..., c n] = n i=1 i 2ord(c i ), ahol a c 1, c 2,... c n értékek a kódolandó jelszó karakterei.
Feladatok 1. kulcsképző függvény: a jelszóból a kulcsot úgy képezzük, hogy a jelszót összefűzzük a toldalékkal, majd a saját megfordítottjával. 2. kulcsképző függvény: a függvénynek három paramétere van: a salt és a jelszó mellett meg kell adni az elérni kívánt hosszúság értékét a jelszó után fűzzük a toldalékot addig, amíg az elérni kívánt hosszúságot el nem értük. Ha a jelszó eleve hosszabb a kívánt hosszúságnál, akkor levágunk belőle. Ha nincs megadva toldalék, akkor magát a jelszót fűzzük egymásután többször, a kívánt hossz eléréséig.
Feladatok A kulcsképző függvények type Salt = String type KDF = Salt -> String -> String kdf1 :: KDF kdf1 salt list = list ++ salt ++ (reverse list) kdf2 :: Int -> KDF...
Feladatok A kulcsképző függvények > kdf1 "" "password" "passworddrowssap" > kdf1 "salt" "password" "passwordsaltdrowssap" > kdf2 16 "" "mypass" "mypassmypassmypa" > kdf2 10 "salt" "mylongpassword" "mylongpass" > kdf2 30 "salt" "mylongpassword" "mylongpasswordsaltsaltsaltsalt"
Feladatok A hash-érték meghatározása a kulcsérték alapján import Data.Char import Data.Bits type Hash = Integer hash :: String -> Hash...
Feladatok A hash-érték meghatározása > hash "passworddrowssap" 13196246709783993782797560045161676800 > hash "passwordsaltdrowssap" 17675583594922209204904521203219169280 > hash "mypassmypassmypa" 65369075091321280138698505461394571264 > hash "mylongpass" 6123727222422612671967307593924214784 > hash "mylongpasswordsaltsaltsaltsalt" 25230113278881150343787438878951997440
Feladatok A jelszó végső hash-értékének létrehozásához a következő adatszerkezettel dolgozunk, el kell tárolni a hash és salt értéket is data Password = Passw Hash Salt deriving (Show) vagy definiálhatjuk a következőképpen is: data Password = P { h :: Hash, s :: Salt } deriving (Show) Jelszó hash-értékének a létrehozása: mkpassword :: KDF -> Salt -> String -> Password mkpassword fugv salt list = Passw (hash $ fugv salt list) salt
Feladatok > mkpassword kdf1 "" "password" Passw 13196246709783993782797560045161676800 "" > mkpassword kdf1 "salt" "password" Passw 17675583594922209204904521203219169280 "salt" > mkpassword (kdf2 16) "" "mypass" Passw 65369075091321280138698505461394571264 "" > mkpassword (kdf2 10) "salt" "mylongpassword" Passw 6123727222422612671967307593924214784 "salt" > mkpassword (kdf2 30) "salt" "mylongpassword" Passw 25230113278881150343787438878951997440 "salt"