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 Hashing (anspruchsvolles Projekt) Programmieren in Haskell 2
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]] 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 index inrange :: (a,a) -> [a] :: (a,a) -> a -> Int :: (a,a) -> a -> Bool rangesize :: (a,a) -> Int range (a,b) liefert eine Liste aller Elemente zwischen a und b: Array> range (1,5) [1,2,3,4,5] index (a,b) c liefert den Index des Wertes c im Bereich (a,b) Array> index (3,5) 3 0 Programmieren in Haskell 6
inrange (a,b) c gibt aus, ob sich der Wert c innerhalb des Bereichs (a,b) befindet Array> inrange (1,5) 3 True rangesize (a,b) liefert die Grösse des Bereichs (a,b) Array> rangesize (1,5) 5 Programmieren in Haskell 7
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 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 Programmieren in Haskell 10
range :: Ix a => (a,a) -> [a] -- liefert die Liste aller Elemente zwischen a und b index :: Ix a => (a,a) -> a -> Int -- liefert den Index eines Wertes im Bereich (a,b) inrange :: Ix a => (a,a) -> a -> Bool -- bestimmt, ob sich ein Wert c im Bereich (a,b) befindet rangesize :: Ix a => (a,a) -> Int -- gibt die Groesse des Bereichs (a,b) zurueck array :: Ix a => (a,a) -> [(a,b)] -> Array a b -- erstellt ein Array der Dimension (a,b) bounds :: Ix a => Array a b -> (a,a) -- gibt die Dimensionen eines Arrays zurueck assocs :: Ix a => Array a b -> [(a,b)] -- gibt den Inhalt eines Arrays zurueck (!) :: Ix a => Array a b -> a -> b -- liefert den Wert eines Arrays an einer bestimmten Position Programmieren in Haskell 11
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 12
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 13
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 14
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 15
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 16
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 17
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 18
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 19
Anzeigen eines zweidimensionalen Arrays showarray :: (Show a, Ix b, Ix c, Enum b, Enum c) => Array (c,b) a -> IO () showarray arr = let ((lx,ly),(ux,uy)) = bounds arr row r = concat [ show (arr!(r,c)) ++ " " c <- [ly.. uy]] rows = concat [ row r ++ "\n" r <- [lx.. ux]] in putstrln rows Main> showarray (pascalstriangle 5) 1 0 0 0 0 0 1 1 0 0 0 0 1 2 1 0 0 0 1 3 3 1 0 0 1 4 6 4 1 0 1 5 10 10 5 1 Programmieren in Haskell 20
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 21
Anwendung: Hashing Warum Hashes? (-> schneller Zugriff UND platzsparend) Abstrakter Datentyp Hash (Schnittstelle) Hash-Implementierung als Haskell-Modul Programmieren in Haskell 22
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 23
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 24
Direkte Verkettung k4 k4 k5 k2 k2 k5 Programmieren in Haskell 25
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 26
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 27
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 28
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 29
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 30
Kunden-Hashfunktion Wir "hashen"über die Telefonnummer: instance Hashable Customer where hashmap m (Customer p ) = p mod m Programmieren in Haskell 31
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 32
Das Hash-Modul Programmieren in Haskell 33
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 34
Zwei Module müssen wir importieren: import Array import List Programmieren in Haskell 35
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 36
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 37
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 38
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 39
Für die übrigen Funktionen (und sowieso): siehe Modul Hash.lhs. Anwendung: hash_test.lhs. Programmieren in Haskell 40