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