Funkcionális programozás 4. 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ó? GHC parancsok fenntartott szavak könyvtármodul importálása A Haskell további jellemz i: feltételek egymásba ágyazása a let...in, where kifejezések a case...of kifejezés függvénykompozícíó függvénykiértékelés a $ szimbólummal a $ és a. szimbólumok a tuple típus, könyvtárfüggvények a lista típus, könyvtárfüggvények operátorok, függvények listákon
Mir l lesz szó? a Haskell kiértékelési stratégiája függvények listákon: map, null, lter, reverse take, takewhile, drop, dropwhile, elem, zip, splitat, iterate, repeat, replicate, any, all Haskell modulok, kompilálás
A Haskell kiértékelési stratégiája funkcionális programozási nyelvek esetén kétféle kiértékelési stratégiát ismerünk: lusta (lazy), mohó (eager) a Haskell kiértékelési stratégiája lusta, Lusta kiértékelési stratégia: a legbaloldalibb, legküls redex (redukálható kifejezés) helyettesítése történik el ször, egy alkifejezés csak akkor értékel dik ki, ha szükség van az értékére (ha a kifejezés függvénymegadással kezd dik el bb a függvénydeníció lesz alkalmazva) ez a stratégia mindig megtalálja a normál formát, ha az létezik Pl. Clean, Haskell, Miranda lehetségessé válik a függvények kötetlen deniálása: egy függvény akkor is képes értéket visszaadni, ha egyik argumentuma nem deniált, lehetségessé válik a végtelen adatszerkezetek létrehozása, a mohó kiértékelési stratégiához képest kevésbé hatékony.
A mohó (eager) kiértékelési stratégia a legbaloldalibb, legbels redex, az argumentumok helyettesítése történik meg el ször, nem mindig ér véget a kiértékelési folyamat, hatékonyabb mint a lusta rendszer, Pl. Lisp, SML, Hope, a lusta kiértékelési stratégia hatékonyságát oly módon lehet javítani, hogy az azonos részkifejezéseket megjelöljük, az eredményt megjegyezzük és ahányszor szükség van rá mindig a megjegyzett eredményt használjuk.
Példa, kiértékelési stratégiákra my_inc :: Num a => a -> a my_inc x = x + 1 negyzet :: Num a => a -> a negyzet x = x * x negyzet_inc :: Num a => a -> a negyzet_inc x = negyzet (my_inc x) > negyzet_inc 6 A lusta kiértékelési stratégia: negyzet_inc 6 -> negyzet(my_inc 6) -> (my_inc 6) * (my_inc 6) -> (6 + 1) * (6 + 1) -> 7 * 7 -> 49 A mohó kiértékelési stratégia: negyzet_inc 6 -> negyzet(my_inc 6) -> negyzet(6 + 1) -> negyzet(7) -> 7 * 7 -> 49 ÁRTON Gyöngyvér
1. feladat Határozzuk meg egy lista második elemét second :: [a] -> a second [] = error "ures lista" second [k1] = error "egy elemu" second (k1: k2: ve) = k2 > second [1..10] 2
map :: (a -> b) -> [a] -> [b] A paraméterként megadott függvényt alkalmazza a második paraméterként megadott lista minden elemére. > map (2^) [0..10] [1,2,4,8,16,32,64,128,256,512,1024] my_map1 :: (a -> b) -> [a] -> [b] my_map1 f [] = [] my_map1 f (k: ve) = f k: my_map1 f ve > import Data.Char > my_map1 toupper "abcdefghijklmnop" "ABCDEFGHIJKLMNOP" > my_map1 length ["abcd", "efg", "hijkl"] [4,3,5]
null :: [a] -> Bool A null függvény benne van a standard könyvtárban és megvizsgálja hogy egy lista üres lista-e vagy tartalmaz elemeket. my_null :: [a] -> Bool my_null [] = True my_null (k: ve) = False A null függvény segítségével a map függvény 2. verziója: my_map2 :: (a -> b) -> [a] -> [b] my_map2 f ls = if null ls then [] else (f (head ls)): my_map2 f (tail ls)
2. feladat Adott listabeli elemekre határozzuk meg a páros osztók listáját. paroso :: (Integral a) => a -> (a, [a]) paroso n = (n, [i i <- [2, 4.. (n `div` 2 + 1)], n `rem` i == 0]) > paroso 60 (60,[2,4,6,10,12,20,30]) > my_map2 paroso [50..59] [(50,[2,10]),(51,[]),(52,[2,4,26]),(53,[]),(54,[2,6,18]), (55,[]),(56,[2,4,8,14,28]),(57,[]),(58,[2]),(59,[])]
filter :: (a -> Bool) -> [a] -> [a] Kiválasztja a lista azon elemeit melyek eleget tesznek egy adott feltételnek. > filter isdigit "abcd123rfg456hij" "123456" my_filter1 :: (a -> Bool) -> [a] -> [a] my_filter1 f [] = [] my_filter1 f (k: ve) f k = k: my_filter1 f ve otherwise = my_filter1 f ve > my_filter1 even [1..10] [2,4,6,8,10] > my_filter1 (>0) [10,-5,3,-12,7] [10,3,7]
my_filter kódsor, 2. verzió: my_filter2 :: (a -> Bool) -> [a] -> [a] my_filter2 f ls = let k = head ls ve = tail ls in if null ls then [] else if f k then k: my_filter2 f ve else my_filter2 f ve > import Data.Char > my_filter2 isupper "EMTE-Sapientia" "EMTES"
reverse :: [a] -> [a] megfordítja a lista elemeit > reverse [1..10] [10,9,8,7,6,5,4,3,2,1] my_reverse1 :: [a] -> [a] my_reverse1 [] = [] my_reverse1 (k: ve) = my_reverse1 ve ++ [k] my_reverse2 :: [a] -> [a] my_reverse2 ls = if null ls then [] else my_reverse2 (tail ls) ++ [head ls] > my_reverse1 "helovilag" "galivoleh"
megfordítja a lista elemeit, harmadik verzió my_reverse3 :: [a] -> [a] my_reverse3 ls = my_sreverse3 ls [] where my_sreverse3 [] res = res my_sreverse3 (k : ve) res = my_sreverse3 ve (k: res) > my_reverse3 "helovilag" "galivoleh"
take :: Int -> [a] -> [a] Visszatéríti a második argumentumként megadott lista els n elemét, ahol n a függvény els argumentuma. > take 3 ["abc", "efgh", "ijklmn", "op", "qrst"] ["abc","efgh","ijklmn"] > take 10 ["abc", "efgh", "ijklmn", "op", "qrst"] ["abc","efgh","ijklmn","op","qrst"] > take 10 [ 1/i i <- [1..]] [1.0,0.5,0.3333333333333333,0.25,0.2,0.16666666666666666, 0.14285714285714285,0.125,0.1111111111111111,0.1] > take 10 [ 1/(2^i) i <- [1..]] > [0.5,0.25,0.125,6.25e-2,3.125e-2,1.5625e-2,7.8125e-3,3.90625e-3, 1.953125e-3,9.765625e-4]
my_take kódsor, 1. verzió: my_take1 :: Int -> [a] -> [a] my_take1 n [] = [] my_take1 n (k: ve) n == 0 = [] otherwise = (k: my_take1 (n-1) ve) > my_take1 10 [] [] > my_take1 10 ["abc", "efgh", "ijklmn", "op", "qrst"] ["abc","efgh","ijklmn","op","qrst"]
my_take kódsor, 2. verzió: my_take2 :: Int -> [a] -> [a] my_take2 n ls = let k = head ls ve = tail ls in if null ls then [] else if n == 0 then [] else k: my_take2 (n - 1) ve > my_take2 4 "Sapientia 2017" "Sapi"
takewhile :: (a -> Bool) -> [a] -> [a] Visszatéríti a második argumentumként megadott lista azon prexét, amelyben az elemek eleget tesznek a feltételnek. > takewhile even [2,4,6,8,9,10,12,14] [2,4,6,8] my_takewhile1 :: (a -> Bool) -> [a] -> [a] my_takewhile1 f [] = [] my_takewhile1 f (k: ve) f k = (k: my_takewhile1 f ve) otherwise = [] > my_takewhile1 ( >0 ) [1,2,3,-5,4,6,7] [1,2,3] > import Data.Char > takewhile1 isdigit "1234aedbcde567fgh" "1234"
my_take kódsor, 2. verzió: my_takewhile2 :: (a -> Bool) -> [a] -> [a] my_takewhile2 f ls = if null ls then [] else if f k then k: my_takewhile2 f ve else [] where k = head ls ve = tail ls > length (my_takewhile2 ( /= 0) [1 / (2 ^ i) i <-[1..]]) 1023
drop :: Int -> [a] -> [a] Kitörli a második argumentumként megadott lista els n elemét, ahol n a függvény els argumentuma. > drop 3 ["abc", "efgh", "ijklmn", "op", "qrst"] ["op","qrst"] my_drop :: Int -> [a] -> [a] my_drop n [] = [] my_drop n (k: ve) n == 0 = (k: ve) otherwise = my_drop (n-1) ve > my_drop 10 [] [] > my_drop 10 ["abc", "efgh", "ijklmn", "op", "qrst"] []
dropwhile :: (a -> Bool) -> [a] -> [a] Kitörli a második argumentumként megadott lista azon prexét, amelyben az elemek eleget tesznek a feltételnek. > dropwhile even [2,4,6,8,9,10,12,14] [9,10,12,14] my_dropwhile :: (a -> Bool) -> [a] -> [a] my_dropwhile f [] = [] my_dropwhile f (k: ve) f k = my_dropwhile f ve otherwise = (k: ve)
elem ::(Eq a) => a -> [a] -> Bool Megvizsgálja, hogy egy adott elem szerepel-e a listaelemek között. > elem 'a' "Sapientia University" True > elem 'A' "Sapientia University" False my_elem :: (Eq a) => a -> [a] -> Bool my_elem x [] = False my_elem x (k: ve) x == k = True otherwise = my_elem x ve maganhangzo :: Char -> Bool maganhangzo c = elem c "aeiouaeiou" > my_dropwhile maganhangzo "aedbcdeamiu" "dbcdeamiu"
zip :: [a] -> [b] -> [(a, b)] A bemeneti két lista alapján elempárokból álló listát hoz létre. Az új lista elemszámát a rövidebb lista elemszáma határozza meg. > zip ["abc", "efg"] [1,2,3,5,6,7] [("abc",1),("efg",2)] my_zip :: [a] -> [b] -> [(a, b)] my_zip [] [] = [] my_zip [] li = [] my_zip li [] = [] my_zip (k1: ve1) (k2: ve2) = ((k1, k2): my_zip ve1 ve2) > zip [1..8] "abcdefghijklmn" [(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e'),(6,'f'),(7,'g'),(8,'h')]
splitat :: :: Int -> [a] -> ([a], [a]) A megadott indexérték alapján a bemeneti listát két listára osztja. Az eredmény egy tuple. > splitat 4 [1,2,3,4,5,6,7,8,9] ([1,2,3,4],[5,6,7,8,9]) my_splitat :: Int -> [a] -> ([a], [a]) my_splitat n list = (l1, l2) where l1 = take n list l2 = drop n list > my_splitat 10 "Sapientia University" ("Sapientia ","University")
iterate :: (a -> a) -> a -> [a] Alkalmazza a megadott függvényt kiindulva a kezdeti értékként megadott paraméterb l. > take 10 (iterate (\x -> 2 * x ) 1) [1,2,4,8,16,32,64,128,256,512] repeat :: a -> [a] A megadott elemmel egy végtelen listát határoz meg. > take 5 (repeat "hello") ["hello","hello","hello","hello","hello"] replicate :: Int -> a -> [a] A megadott elemmel egy n elem listát határoz meg. > replicate 5 "hello" ["hello","hello","hello","hello","hello"]
any :: (a -> Bool) -> [a] -> Bool Megvizsgálja, hogy a megadott feltételt teljesíti-e valamelyik listabeli elem. > any isupper "Sapientia University" True all :: (a -> Bool) -> [a] -> Bool Megvizsgálja, hogy a megadott feltételt teljesíti-e minden listabeli elem. > all isupper "Sapientia University" False
Haskell modulok Nagyobb program esetén a projektet részekre kell bontani, ezeket a részegységeket hívjuk moduloknak (más programozási nyelvben is így van). A modul els sora a module kulcsszót és a module nevét tartalmazza, nagy kezd bet vel, utána a where kulcsszó, majd az importok és a további kódsorok következnek: -- filename: Eloadas5.hs -- author: Marton Gyongyver -- date: 11.03.2015 module Eloadas5 where import Eloadas3 import Data.Char import Data.List Egy állományba csak egy modult írjunk.
Haskell modulok Ha a modulok függetlenek, akkor lehet egy projektet készíteni. module Main where import Eloadas3 import Eloadas4 import Eloadas5 import Data.Char import Data.List main = print (convert16 458729) Minden modulban importalni kell azt a modult, amelyben annak a függvénynek a deniciója van, amit szeretnénk használni.
Haskell modulok, komplilálás A parancssorból való futtatást több lépésb l oldjuk meg: 1. lépés: a megfelel f modul (Main) betöltése: : load Main.hs 2. lépés: az értelmez ben ha megakarom hívni: > main 6FFE9 3. lépés: a f modul kompilálása, az exe létrehozása windows alatt: :! ghc --make "Main.hs" -o main.exe 4. lépés: ezután lehet futtatni parancssorból is