Programmieren in Haskell Felder (Arrays) Programmieren in Haskell 1
Was wir heute machen Motivationsbeispiel Die Typklasse Ix Felder in Haskell Funktionstabellierung Binäre Suche Pascalsches Dreieck Ein lineares Sortierverfahren Hashing (ansprochsvolles Projekt) Programmieren in Haskell 2
Quadratzahlen 0 1 2 3 n 0 1 4 9 n 2 Programmieren in Haskell 3
Quadratzahlen 0 1 2 3 n 0 1 4 9 n 2 squareslist :: Integral a => [a] squareslist = [n^2 n <- [0..]] Programmieren in Haskell 3
Quadratzahlen 0 1 2 3 n 0 1 4 9 n 2 squareslist :: Integral a => [a] squareslist = [n^2 n <- [0..]] Zugriff auf die n-te Quadratzahl mit squareslist!!n, z.b. squareslist!!5 => 25 Programmieren in Haskell 3
Quadratzahlen 0 1 2 3 n 0 1 4 9 n 2 squareslist :: Integral a => [a] squareslist = [n^2 n <- [0..]] Zugriff auf die n-te Quadratzahl mit squareslist!!n, z.b. squareslist!!5 => 25 Laufzeit? Programmieren in Haskell 3
Quadratzahlen 0 1 2 3 n 0 1 4 9 n 2 squareslist :: Integral a => [a] squareslist = [n^2 n <- [0..]] Zugriff auf die n-te Quadratzahl mit squareslist!!n, z.b. squareslist!!5 => 25 Laufzeit? Θ(n) Programmieren in Haskell 3
Quadratzahlen mit Feldern (Arrays) squaresarray :: (Integral a, Ix a) => Array a a squaresarray = array (0,99) [(n,n^2) n <- [0..99]] Programmieren in Haskell 4
Quadratzahlen mit Feldern (Arrays) squaresarray :: (Integral a, Ix a) => Array a a squaresarray = array (0,99) [(n,n^2) n <- [0..99]] Zugriff auf die n-te Quadratzahl mit squaresarray!n, z.b. squaresarray!5 => 25 Programmieren in Haskell 4
Quadratzahlen mit Feldern (Arrays) squaresarray :: (Integral a, Ix a) => Array a a squaresarray = array (0,99) [(n,n^2) n <- [0..99]] Zugriff auf die n-te Quadratzahl mit squaresarray!n, z.b. squaresarray!5 => 25 Laufzeit? Programmieren in Haskell 4
Quadratzahlen mit Feldern (Arrays) squaresarray :: (Integral a, Ix a) => Array a a squaresarray = array (0,99) [(n,n^2) n <- [0..99]] Zugriff auf die n-te Quadratzahl mit squaresarray!n, z.b. squaresarray!5 => 25 Laufzeit? Θ(1) Programmieren in Haskell 4
Die Klasse Ix in der Typhierarchie Enum Eq Show / \ / Ord Num / \ / \ Ix Real Fractional / Integral Programmieren in Haskell 5
Interface der Klasse Ix class Ord a => Ix a where range :: (a,a) -> [a] index :: (a,a) -> a -> Int inrange :: (a,a) -> a -> Bool rangesize :: (a,a) -> Int Minimal complete instance : range, index, inrange Programmieren in Haskell 6
Annahmen über Ix-Instanzen inrange (l,u) i == elem i (range (l,u)) range (l,u)!! index (l,u) i == i, when inrange (l,u) i map (index (l,u)) (range (l,u))) == [0..rangeSize (l,u)-1] rangesize (l,u) == length (range (l,u)) Programmieren in Haskell 7
Beispiel data Abc = A B C deriving (Show,Eq,Ord) Programmieren in Haskell 8
Beispiel data Abc = A B C deriving (Show,Eq,Ord) instance Ix Abc where range (A,A) = [A] range (A,B) = [A,B] range (A,C) = [A,B,C] range (B,B) = [B] range (B,C) = [B,C] range (C,C) = [C] range _ = [] index (l,u) a = find 0 a (range (l,u)) where find i a (x:xs) a == x = i a > x = find (i+1) a xs find i a _ = error "Ix.index: Index out of range." inrange (l,u) a = l <= a && a <= u Programmieren in Haskell 8
range (A,C) => [A,B,C] index (A,C) B => 1 index (B,C) A => Program error: Ix.index: Index out of range. inrange (B,C) A => False Programmieren in Haskell 9
range (A,C) => [A,B,C] index (A,C) B => 1 index (B,C) A => Program error: Ix.index: Index out of range. inrange (B,C) A => False Ix-Instanzen sind auch ableitbar (für Aufzählungstypen und für Ein-Konstruktor-Typen deren Argument-Typen Instanzen von Ix sind): data Abc = A B C deriving (Show,Eq,Ord,Ix) Programmieren in Haskell 9
Funktionen auf Indextypen range :: Ix a => (a,a) -> [a] index :: Ix a => (a,a) -> a -> Int inrange :: Ix a => (a,a) -> a -> Bool rangesize :: Ix a => (a,a) -> Int array :: Ix a => (a,a) -> [(a,b)] -> Array a b bounds :: Ix a => Array a b -> (a,a) assocs :: Ix a => Array a b -> [(a,b)] (!) :: Ix a => Array a b -> a -> b Programmieren in Haskell 10
array/bounds/assocs/(!) afewsquares = array (0,4) [(n,n^2) n <- [0..4]] bounds afewsquares => (0,4) assocs afewsquares => [(0,0),(1,1),(2,4),(3,9),(4,16)] afewsquares!4 => 16 Programmieren in Haskell 11
Funktionstabellierung tabulate :: Ix a => (a -> b) -> (a,a) -> Array a b tabulate f bs = array bs [(i, f i) i <- range bs] Programmieren in Haskell 12
Anwendung: Tabellierung badfib :: Integral a => a -> a badfib 0 = 1 badfib 1 = 1 badfib n = badfib (n-2) + badfib (n-1) fib :: (Integral a, Ix a) => a -> a fib n = t!n where t = tabulate f (0,n) f 0 = 1 f 1 = 1 f n = t!(n-2) + t!(n-1) Programmieren in Haskell 13
Listen zu Felder listarray :: Ix a => (a,a) -> [b] -> Array a b -- vordefiniert listarray bs vs = array bs (zip (range bs) vs) zip :: [a] -> [b] -> [(a,b)] -- vordefiniert zip [] [] = [] zip [] (y:ys) = [] zip (x:xs) [] = [] zip (x:xs) (y:ys) = (x,y):zip xs ys zip [1..5] [ a.. z ] => [(1, a ),(2, b ),(3, c ),(4, d ),(5, e )] Programmieren in Haskell 14
Typ Ordering data Ordering = LT EQ GT -- vordefiniert compare :: Ord a => a -> a -> Ordering -- vordefiniert compare a b a < b = LT a == b = EQ a > b = GT Programmieren in Haskell 15
Anwendung: Binäre Suche binarysearch :: (Ord b, Integral a, Ix a) => Array a b -> b -> Bool binarysearch a e = within (bounds a) where within (l,r) = l <= r && let m = (l + r) div 2 in case compare e (a!m) of LT -> within (l, m-1) EQ -> True GT -> within (m+1, r) Programmieren in Haskell 16
Anwendung: Pascalsches Dreieck 0 1 2 3 4 5 6 7 8 0 1 1 1 1 2 1 2 1 3 1 3 3 1 4 1 4 6 4 1 5 1 5 10 10 5 1 6 1 6 15 20 15 6 1 7 1 7 21 35 35 21 7 1 8 1 8 28 56 70 56 28 8 1 Programmieren in Haskell 17
pascalstriangle :: Int -> Array (Int,Int) Int pascalstriangle n = a where a = array ((0,0),(n,n)) ( [((i,j),0) i <- [0..n], j <- [i+1..n]] ++ [((i,0),1) i <- [0..n]] ++ [((i,i),1) i <- [1..n]] ++ [((i,j),a!(i-1,j) + a!(i-1,j-1)) i <- [2..n], j <- [1..i-1]]) Programmieren in Haskell 18
Randbemerkung: Binomialkoeffizienten (x + y) n = nx k=0! n x k y n k k! n k = 8 < : n! (n k)!k! 0 k n 0 0 n < k, Programmieren in Haskell 19
Akkumulierende Felder accumarray :: Ix a => (b -> c -> b) -> b -> (a,a) -> [(a,c)] -> Array a b -- vordefiniert Der Ausdruck accumarray (*) e bs vs ergibt das Feld a, wobei das Feldelement a!i gleich ( ((e*c 1 )*c 2 ) )*c k ist, wenn vs dem Index i nacheinander die Werte c 1,..., c k zuordnet. Beachte: Ist die Operation (*) nicht kommutativ, spielt die Reihenfolge der Elemente in vs eine Rolle. Programmieren in Haskell 20
Anwendung: Ein lineares Sortierverfahren countingsort :: Ix a => (a,a) -> [a] -> [a] countingsort bs x = [ a (a,n) <- assocs t, i <- [1..n]] where t = accumarray (+) 0 bs [(a,1) a <- x, inrange bs a] Programmieren in Haskell 21
Anwendung: Ein lineares Sortierverfahren countingsort :: Ix a => (a,a) -> [a] -> [a] countingsort bs x = [ a (a,n) <- assocs t, i <- [1..n]] where t = accumarray (+) 0 bs [(a,1) a <- x, inrange bs a] Effizienz? Programmieren in Haskell 21
Anwendung: Ein lineares Sortierverfahren countingsort :: Ix a => (a,a) -> [a] -> [a] countingsort bs x = [ a (a,n) <- assocs t, i <- [1..n]] where t = accumarray (+) 0 bs [(a,1) a <- x, inrange bs a] Effizienz? Wenn das Intervall bs die Größe m und x die Länge n hat, sortiert countingsort in Θ(m + n). Programmieren in Haskell 21
Und Listen von Listen? listsort :: (Ix a) => (a, a) -> [[a]] -> [[a]] listsort bs xs drop 8 xs == [] = isort xs otherwise = [[] [] <- xs] ++ [a:x (a, ys) <- assocs t, x <- listsort bs ys] where t = accumarray (\y b -> b:y) [] bs [(a,x) (a:x) <- xs] Programmieren in Haskell 22
Und Listen von Listen? listsort :: (Ix a) => (a, a) -> [[a]] -> [[a]] listsort bs xs drop 8 xs == [] = isort xs otherwise = [[] [] <- xs] ++ [a:x (a, ys) <- assocs t, x <- listsort bs ys] where t = accumarray (\y b -> b:y) [] bs [(a,x) (a:x) <- xs] listsort ( A, z ) ["bla","blub","hallo","welt","marc","robert","hund", "Katze","Maus","eins","zwei","drei","wunderbar"] => ["Hund","Katze","Marc","Maus","Robert","bla","blub","drei","eins","hallo", "welt","wunderbar","zwei"] Programmieren in Haskell 22
Array-Update (//) :: (Ix a) => Array a b -> [(a, b)] -> Array a b unitmatrix :: (Ix a, Num b) => (a,a) -> Array (a,a) b unitmatrix bs@(l,r) = array bs [(ij,0) ij <- range bs ] // [((i,i),1) i <- range bs] where bs = ((l,l),(r,r)) Programmieren in Haskell 23
Anwendung: Hashing Warum Hashes? (-> schneller Zugriff UND platzsparend) Abstrakter Datentyp Hash (Schnittstelle) Hash-Implementierung als Haskell-Modul Programmieren in Haskell 24
Direkte Adressierung (kein Hashing) U (Universum der Schl"ussel) 0 1 9 4 0 7 6 2 3 1 K (Aktuelle Schl"ussel) 5 2 3 4 5 6 7 8 8 9 T Programmieren in Haskell 25
Hashing 0 U (Universum der Schl"ussel) h(k1) h(k4) K (Aktuelle Schl"ussel) k2 k4 k3 k1 k5 h(k2) = h(k5) h(k3) T m 1 Programmieren in Haskell 26
Direkte Verkettung k4 k4 k5 k2 k2 k5 Programmieren in Haskell 27
Hash-Schnittstelle emptyhash :: Int -> Hash a capacity :: Hash a -> Int loadfactor :: Fractional b => Hash a -> b insert :: (Eq a, Hashable a) => a -> Hash a -> Hash a contains :: (Eq a, Hashable a) => a -> Hash a -> Bool lookup :: (Eq a, Hashable a) => a -> Hash a -> a hashlist :: Hash a -> [a] delete :: (Eq a, Hashable a) => a -> Hash a -> Hash a update :: (Eq a, Hashable a) => a -> Hash a -> Hash a Programmieren in Haskell 28
Klasse Hashable Instanzen von Hashable haben eine Hash-Funktion definiert: class Hashable a where hashmap :: Int -> a -> Int -- hash function -- first argument is hash capacity Programmieren in Haskell 29
Annahmen über Hashes (fällt uns da noch mehr ein?) hashlist (emptyhash m) == [] for any hash capacity m contains x (insert x h) == True, if contains x h == False lookup x h == x, if contains x h == True (bag. hashlist) (delete x (insert x h)) == (bag. hashlist) h, if contains x h == False (Die Funktion bag wandelt eine Liste in eine Multimenge um.) Programmieren in Haskell 30
Ein einfacher Int-Hash instance Hashable Int where hashmap m x = x mod m inthash :: Hash Int inthash = insert 1 $ insert 2 $ insert 120 $ emptyhash 10 allints :: [Int] allints = hashlist inthash Programmieren in Haskell 31
Ein Kunden-Hash type Phone = Int type Name = String type Address = String data Customer = Customer Phone Name Address deriving Show Zwei Kunden sind gleich gdw. ihre Telefonnummern gleich sind (das ist keine Feststellung, sondern eine Definition): instance Eq Customer where (==) (Customer p ) (Customer q ) = p == q Programmieren in Haskell 32
Kunden-Hashfunktion Wir "hashen"über die Telefonnummer: instance Hashable Customer where hashmap m (Customer p ) = p mod m Programmieren in Haskell 33
customerhash :: Hash Customer customerhash = insert (Customer 13 "Robert" "Uni") $ insert (Customer 3 "Marc" "Uni") $ emptyhash 10 phone3customer :: Customer phone3customer = Hash.lookup (Customer 3 "" "") customerhash updatedcustomerhash :: Hash Customer updatedcustomerhash = update (Customer 3 "Marc" "zu Hause") customerhash updatedphone3customer :: Customer updatedphone3customer = Hash.lookup (Customer 3 "" "") updatedcustomerhash Programmieren in Haskell 34
Das Hash-Modul Programmieren in Haskell 35
module Hash( Hash, Hashable, emptyhash, capacity, loadfactor, Hash.insert, contains, Hash.lookup, hashlist, Hash.delete, update ) where -- We export only type constructor Hash, not data constructor Ha -- Thus the client cannot select the hash representation. Note: -- would export both by writing Hash(Hash). -- qualified because of ambiguity with Data.List.insert -- qualified because of ambiguity with Hugs.Prelude.lookup -- qualified because of ambiguity with Data.List.delete Programmieren in Haskell 36
Zwei Module müssen wir importieren: import Array import List Programmieren in Haskell 37
Datentyp Hash newtype Hash a = Hash (Array Int [a]) -- representation of hash as array instance Show (Hash a) where show h = "Hash" -- we don t want to show anything here Programmieren in Haskell 38
emptyhash m = Hash (array (0,m-1) [(i,[]) i <- [0..m-1]]) capacity (Hash a) = m + 1 where (0,m) = bounds a Programmieren in Haskell 39
insert x h@(hash a) contains x h = error "Hash.insert: element already in hash." otherwise = Hash (a // [(i,x:a!i)]) where m = capacity h i = hashmap m x lookup x h@(hash a) contains x h = head $ filter (==x) (a!(hashmap m x)) otherwise = error "Hash.lookup: hash does not contain element." where m = capacity h Programmieren in Haskell 40
hashlist (Hash a) = concat $ map snd (assocs a) update x h@(hash a) contains x h = Hash (a // [(i,x:(a!i \\ [x]))]) otherwise = error "Hash.update: hash does not contain element." where m = capacity h i = hashmap m x Programmieren in Haskell 41
Für die übrigen Funktionen (und sowieso): siehe Modul Hash.lhs. Anwendung: hash_test.lhs. Programmieren in Haskell 42