Functional Programming
Transkrypt
Functional Programming
Programowanie w Haskellu Wykład 4 – Definiowanie list 1 Zbiory w matematyce W matematyce znana jest notacja służąca do definiowania zbiorów (ang. comprehension) na podstawie innych zbiorów. {x2 | x {1...5}} Określa zbiór {1,4,9,16,25} wszystkich liczb x2 takich, że x należy do zbioru {1…5}. 2 „Lists Comprehensions” W Haskellu można podobnie postąpić z listami. [x^2 | x <- [1..5]] Definiuje listę [1,4,9,16,25] wszystkich liczb x^2 takich, że x jest elementem listy [1..5]. 3 Uwagi: Wyrażenie x <- [1..5] zwane jest generatorem, gdyż określa jak uzyskać kolejne wartości x. Generatorów może być więcej niż jeden. Na przykład: > [(x,y) | x <- [1,2,3], y <- [4,5]] [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)] 4 Kolejność ich umieszczenia nie jest bez znaczenia: > [(x,y) | y <- [4,5], x <- [1,2,3]] [(1,4),(2,4),(3,4),(1,5),(2,5),(3,5)] Można tu zauważyć analogię do zagnieżdżonych pętli: generatory umieszczone na końcu są odpowiednikiem pętli najbardziej zagnieżdżonych. 5 Na przykład: > [(x,y) | y <- [4,5], x <- [1,2,3]] [(1,4),(2,4),(3,4),(1,5),(2,5),(3,5)] x <- [1,2,3] jest ostatnim generatorem, dlatego pierwsza składowa każdej pary zmienia się z największą częstością. 6 Generatory zależne Generator może być uzależniony od wartości zwracanych przez wcześniejsze generatory. [(x,y) | x <- [1..3], y <- [x..3]] Definiuje [(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)] wszystkie pary (x,y) takie, że x,y są elementami listy [1..3] oraz y x. 7 Generatory zależne wykorzystane zostały do definicji konkatenacji listy list: concat :: [[a]] -> [a] concat xss = [x | xs <- xss, x <- xs] Na przykład: > concat [[1,2,3],[4,5],[6]] [1,2,3,4,5,6] 8 Dodatkowe warunki Definiując listę, oprócz generatorów można wstawiać warunki dotyczące wartości generatorów. [x | x <- [1..10], even x] Określa [2,4,6,8,10] wszystkie liczby x takie, że x jest elementem listy [1..10] i x jest parzyste. 9 Jako ilustrację łączenia generatorów i warunków rozważmy funkcję, która dla całkowitego x zwraca listę dzielników x mniejszych od x: dzielniki :: Integer -> [Integer] dzielniki n = [x | x <- [1..n-1], n `mod` x == 0] Na przykład: > dzielniki 15 [1,3,5] 10 Korzystając z poprzedniej funkcji, bardzo łatwo zdefiniować funkcję sprawdzającą czy podana liczba jest liczbą pierwszą: pierwsza :: Int -> Bool pierwsza n = dzielniki n == [1,n] Na przykład: > pierwsza 15 False > pierwsza 7 True 11 Kolejna ilustracja połączenia generatorów i warunków: pierwsze :: Int -> [Int] pierwsze n = [x | x <- [2..n], pierwsza x] Na przykład: > pierwsze 40 [2,3,5,7,11,13,17,19,23,29,31,37] 12 Standardowa funkcja zip Bardzo użyteczna jest funkcja zip, która z dwóch list tworzy listę par elementów. zip :: [a] -> [b] -> [(a,b)] Na przykład: > zip [’a’,’b’,’c’] [1,2,3,4] [(’a’,1),(’b’,2),(’c’,3)] 13 Za pomocą funkcji zip możemy zdefiniować funkcję zwracającą pary sąsiadujących ze sobą elementów: pary :: [a] -> [(a,a)] pary xs = zip xs (tail xs) Na przykład: > pary [1,2,3,4] [(1,2),(2,3),(3,4)] 14 Korzystając z funkcji „pary” łatwo można zdefiniować funkcję sprawdzającą, czy tablica jest uporządkowana: posortowana :: Ord a => [a] -> Bool posortowana xs = and [x <= y | (x,y) <- pary xs] Na przykład: > posortowana [1,2,3,4] True > posortowana [1,3,2,4] False 15 Korzystając z funkcji zip możemy uzyskać listę indeksów elementów równych danemu x: pozycje :: Eq a => a -> [a] -> [Int] pozycje x xs = [i | (x’,i) <- zip xs [0..n], x == x’] where n = length xs - 1 Na przykład: > pozycje 0 [1,0,0,1,0,1,1,0] [1,2,4,7] 16 Stringi String jest ciągiem znaków ujętym w cudzysłów. W Haskellu reprezentowany jest jako lista znaków. "abc" :: String Oznacza [’a’,’b’,’c’] :: [Char]. 17 Ponieważ stringi są specjalnymi listami, każda polimorficzna funkcja działająca na liście będzie działała na stringu. Na przykład: > length "abcde" 5 > take 3 "abcde" "abc" > zip "abc" [1,2,3,4] [(’a’,1),(’b’,2),(’c’,3)] 18 W funkcjach operujących na łańcuchach, podobnie jak przy zwykłych listach, możemy korzystać z generatorów i warunków : import Char ile_malych :: String -> Int ile_malych xs = length [x | x <- xs, isLower x] Na przykład: > ile_malych "Haskell" 6 19 Ćwiczenia (1) Jaki ciąg liczbowy został zdefiniowany przez funkcję f ? f = 0 : 1 : zipWith (+) f (tail f) 20 (2) Jak nazywamy elementy ciągu g: g :: [Integer] zdefiniowanego w następujący sposób: g = [x | x <- [1..], x == sum (dzielniki x)] 21 (3) Jak nazywamy elementy ciągu h n dla całkowitego n>0 ? h :: Integer -> [[Integer]] h n = [x | k where h' n k | | | <- [1..n], x <- h' n k] n < k = [] n == k = [[n]] otherwise = [k : xs | j <- [k,k-1..1], xs <- h' (n-k) j] 22