Kapitel 7: Felder

Werbung
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
0 1 4 9 ···
n
n2
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
range
index
inRange
rangeSize
=>
::
::
::
::
Ix a where
(a,a) -> [a]
(a,a) -> a -> Int
(a,a) -> a -> Bool
(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
index
index
inRange
(A,C)
=> [A,B,C]
(A,C) B => 1
(B,C) A => Program error: Ix.index: Index out of range.
(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
badfib
badfib
badfib
:: Integral a => a -> a
0 = 1
1 = 1
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
zip
zip
zip
zip
:: [a] -> [b] -> [(a,b)] -- vordefiniert
[] []
= []
[] (y:ys) = []
(x:xs) [] = []
(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
Programmieren in Haskell
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
7 1
7
21
35
35
21
7 1
8 1
8
28
56
70
56
28 8
1
1
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)
0
U
(Universum der Schl"ussel)
0
9
6
2
7
4
1
1
3
4
K
(Aktuelle Schl"ussel)
2
5
3
6
5
7
8
8
9
T
Programmieren in Haskell
23
Hashing
0
U
h(k1)
(Universum der Schl"ussel)
h(k4)
k1
K
(Aktuelle Schl"ussel)
h(k2) = h(k5)
k4
k2
k5
h(k3)
k3
m−1
T
Programmieren in Haskell
24
Direkte Verkettung
k4
k4
k2
k2
k5
k5
Programmieren in Haskell
25
Hash-Schnittstelle
emptyHash
capacity
loadFactor
insert
contains
lookup
hashList
delete
update
Programmieren in Haskell
::
::
::
::
::
::
::
::
::
Int -> Hash a
Hash a -> Int
Fractional b =>
(Eq a, Hashable
(Eq a, Hashable
(Eq a, Hashable
Hash a -> [a]
(Eq a, Hashable
(Eq a, Hashable
Hash a -> b
a) => a -> Hash a -> Hash a
a) => a -> Hash a -> Bool
a) => a -> Hash a -> a
a) => a -> Hash a -> Hash a
a) => a -> Hash a -> Hash a
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
type
type
data
Phone
Name
Address
Customer
=
=
=
=
Int
String
String
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
Programmieren in Haskell
-- 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
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
Herunterladen