Universität Bielefeld Programmieren in Haskell Giegerich Programmieren in Haskell WS 2011/2012 Guards Arrays Primzahlen Sortieren RobertGiegerich Universität Bielefeld AG Praktische Informatik November 12, 2013 Haskell-Syntax: Ergänzungen Universität Bielefeld Programmieren in Haskell Giegerich Guards Es gibt noch etwas bequeme Notation für Fallunterscheidungen, die wir bisher nicht benutzt haben. Bisher kennen wir: in Ausdrücken: if condition then ausdruck else ausdruck in Definitionen: Muster (pattern matching) in Gleichungen Arrays Primzahlen Sortieren Neue Fallunterscheidungen rechts ... Pattern matching in Ausdrücken: 1 2 3 4 5 Einfache Form case expr pat1 -> pat2 -> pat3 -> ... Universität Bielefeld Programmieren in Haskell Giegerich of expr1 expr2 expr3 Guards Arrays Primzahlen Sortieren Example 1 2 3 4 5 6 > endswith :: String -> String > endswith xs = case ( reverse xs ) of > [] -> " no end at all ! " > a : as -> if ’A ’ <= a && a <= ’Z ’ then " capita > else if ’a ’ <= a && a <= ’z ’ then " small > else " not a letter . " Neue Fallunterscheidungen rechts ... 1 2 3 4 5 jetzt zusätzlich mit Wächtern: case expr of pat1 | guard1 -> expr1 pat2 | guard2 -> expr2 pat3 | guard3 -> expr3 ... Universität Bielefeld Programmieren in Haskell Giegerich Guards Arrays Primzahlen Sortieren Example 1 2 3 4 5 6 > endswith ’:: String -> String > endswith ’ xs = case ( reverse xs ) > [] -> > a : as | ’A ’ <= a && a <= ’Z ’ -> > a : as | ’a ’ <= a && a <= ’z ’ -> > _ -> of " no end at all ! " " capital letter . " small letter . " " not a letter . " ... und links: Wächter in Funktionsdefinitionen 1 2 3 4 Bewachte Gleichungen > fn pat1 pat2 ... > | guard1 = expr1 > | guard2 = expr2 > ... syntaktische Zucker für Funktionsdefinitionen guards (Wächter) sind Ausdrücke (vom Typ Bool) sie ersetzen geschachtelte if-Ausdrücke auf der rechten Seite Example 1 2 3 4 howManyEqual n m p | ( n == m ) && ( m == p ) = 3 | ( n == m ) || ( m == p ) || ( n == p ) = 2 | otherwise = 0 Universität Bielefeld Programmieren in Haskell Giegerich Guards Arrays Primzahlen Sortieren Felder: Datenstrukturen mit konstantem Zugriff Universität Bielefeld Programmieren in Haskell “Felder” nennt man auch Arrays, Vektoren, Matrizen, ... “Konstanter Zugriff” heisst: Zugriff auf Elemente mit Aufwand unabhängig von der Größe der Datenstruktur. Giegerich Guards Arrays Primzahlen Sortieren Gegenbeispiel Musik: Finden der n-ten Note erfordert Durchlauf der Datenstruktur in n oder mehr Schritten. Allerdings: Die Noten sind nicht nummeriert. Besseres Gegenbeispiel: Listen – hier sind die Elemente ab 0 nummeriert ... Zugriff auf Listenelemente Universität Bielefeld Programmieren in Haskell Giegerich Listen beherbergen viele Elemente gleichen Typs. 1 2 1 2 Sequentielle Abarbeitung ist einfach und effizient, z.B. map f [] = [] map f ( x : xs ) = f x : map f xs Random Zugriff auf einzelne Elemente durch operator !! hängt von der Listenlänge ab: ( x : xs ) !! 0 = x ( x : xs ) !! n = xs !!( n -1) Abhilfe schafft der Datentyp Array a b Guards Arrays Primzahlen Sortieren Datentyp Array a b Universität Bielefeld Programmieren in Haskell Arrays – auf Deutsch “Felder” Modul Array stellt Datentyp Array a b und Funktionen darauf bereit a ist der Datentyp des Index, b der Elementtyp Operator ! für element-Zugriff bietet effizienten Random-Access Indextyp erlaubt mehrere Dimensionen Funktion array liefert ein Array zurück 1 array : : ( I x a ) => ( a , a ) −> [ ( a , b ) ] −> Array a b 2 3 t = a r r a y ( low , h i g h ) l i s t _ o f _ e n t r i e s = . . . 1 (!) :: ( Ix a ) = > Array a b -> a -> b Giegerich Guards Arrays Primzahlen Sortieren Beispiel: 2D Array 1 2 3 > module A r r > where > import Array 4 5 6 7 8 9 10 11 12 13 14 15 16 19 Programmieren in Haskell Giegerich Guards > t a b l e : : Array ( Char , I n t ) I n t > t a b l e = array ( ( ’ a ’ , 0) , ( ’ c ’ , 2) ) > [ ( ( ’ a ’ , 0) , 0 ) , > ( ( ’ a ’ , 1) , 1 ) , > ( ( ’ a ’ , 2) , 2 ) , > ( ( ’b ’ , 0) , 3 ) , > ( ( ’b ’ , 1) , 4 ) , > ( ( ’ c ’ , 0) , 6 ) , > ( ( ’ c ’ , 1) , 7 ) , > ( ( ’b ’ , 2) , 5 ) , > ( ( ’ c ’ , 2) , 8 ) > ] 17 18 Universität Bielefeld > access a b = table ! (a , b) > access2 = ( table !) Arrays Primzahlen Sortieren Alternative Universität Bielefeld Programmieren in Haskell Giegerich Pattern Matching als Alternative für read-only Felder Guards vs. Feldgröße Arrays Primzahlen Sortieren Example 1 2 3 4 5 access access access access ... ’a ’ ’a ’ ’a ’ ’b ’ 0 1 2 1 = = = = 0 1 2 3 Beispiel: Umwandlung String in Char Array Universität Bielefeld Programmieren in Haskell Giegerich Guards Arrays Primzahlen 1 2 3 Sortieren > s 2 a : : S t r i n g −> Array I n t Char > s2a xs = l i s t A r r a y (0 , length xs − 1) xs Akkumulierende Arrays Universität Bielefeld Programmieren 1 2 in Haskell Manchmal kann man die Einträge eines Feldes nicht auf einmal Giegerich ausrechnen. Dafür gibt es die Funktion > accumArray :: Ix a = > Guards ( b -> c -> b ) -> b -> (a , a ) -> [( a , c )] -> Array Arraysa b Primzahlen 3 4 5 akkumulierende Bereich Fkt . Startwert Elemente Der Aufruf accumArray f s (lo,up) assocslist erlaubt fehlende wie mehrfache Auftreten des Indices in den Associations initialisiert alle Elemente mit dem Startwert s akkumuliert mehrfache Auftreten (i,x),...,(i,y),...,(i,z) in der Form (((s ‘f‘ x) ‘f‘ y) ‘f‘ z) in Element i Sortieren Ergebnis Akkumulierende Arrays Universität Bielefeld Programmieren in Haskell Giegerich 1 2 3 Beispiel: Zählen der Buchstabenhäufigkeit in einem Text Guards > frq :: String -> Array Char Int Arrays > frq t = accumArray (+) 0 ( ’A ’ , ’z ’) ( zip t ones ) Primzahlen > where ones = 1: ones Sortieren 1 2 3 4 Damit z.B. Arr > frq " abraham " ! ’a ’ 3 Arr > frq " abraham " ! ’U ’ 0 Tabellierung von Funktionen Gegeben eine Funktion, die aufwendig zu berechnen ist: 1 2 3 > sid : : I n t −> I n t > s i d x = ( x +1)∗( x −1)−(x −2)∗( x+2)+(x−round 3 . 1 ) Universität Bielefeld Programmieren in Haskell Giegerich Guards Arrays Primzahlen Sortieren Wenn die Werte (aus einem bekannten Intervall) immer wieder gebraucht werden, nimmt man eine interne Tabelle 1 2 3 4 5 6 > fid : : I n t −> I n t > f i d = ( f ! ) where > f = array (0 ,100) > [ ( x , ( x +1)∗( x −1)−(x −2)∗( x+2)+(x−round 3 . 1 ) ) > | x <− [ 0 . . 1 0 0 ] ] Das funktioniert schneller, aber nur über dem vorgesehenen Intervall. Tabellierung von Funktionen Universität Bielefeld Programmieren in Haskell Tabellierung ist eine allgemeine Technik. Können wir eine Funktion tabulate schreiben, die andere Funktionen in tabellierte Funktionen verwandelt? Was wäre ihr Typ? Giegerich Guards Arrays Primzahlen 1 2 > t a b u l a t e : : ( I n t , I n t ) −> ( I n t −> a ) −> ( I n t −> a ) Und was muss sie tun? Wir fordern ∀ x, f: (tabulate f) x = f x – nur schneller! 1 2 3 > t a b u l a t e ( l o , up ) f = ( t ! ) where > t = a r r a y ( l o , up ) ( z i p domain (map f domain ) ) > domain = [ l o . . up ] Sortieren Tabellierung von Funktionen Universität Bielefeld Programmieren in Haskell Giegerich Damit nun einfach – für beliebige Funktionen über einem Intervall, aber hier am Beispiel sid Guards > t i d : : I n t −> I n t > t i d = tabulate (1 ,100) s i d Sortieren Arrays Primzahlen 1 2 3 Betrachte die Anzahl der Rechenschritte bei mehrfachen Aufrufen von sid 42, fid 42, tid 42 ... Primzahlen Universität Bielefeld Programmieren in Haskell Giegerich Primzahl Guards natürliche Zahl > 1 Arrays nur teilbar durch 1 und sich selbst Primzahlen Erster Versuch: Hier ist direkt die Definition progammiert: 1 2 3 4 5 > primesSlow > primesSlow > where > divisors > divisors : : I n t e g r a l a => [ a ] = [ n | n <− [ 2 . . ] , d i v i s o r s n == [ 1 , n ] ] : : I n t e g r a l a => a −> [ a ] n = [ d | d <− [ 1 . . n ] , n ‘mod ‘ d == 0 ] Sieb des Eratosthenes Sortieren Sieb des Eratosthenes Universität Bielefeld Programmieren in Haskell Giegerich Aus der Liste aller Zahlen werden sukzessive die Vielfachen der bereits gefundenen Primzahlen herausgesiebt ... Guards Arrays Primzahlen Sieb des Eratosthenes 1 2 > primes :: Integral a = > [ a ] > primes = sieve [2..] where Sortieren 3 4 5 > > sieve :: Integral a = > [ a ] -> [ a ] sieve ( a : xs ) = a : sieve [ n |n < - xs , n ‘ mod ‘ a /= 0] Sortieren Universität Bielefeld Programmieren in Haskell Spezifikation des Sortierproblems: genauer: Sortieren per Vergleich Eingabe: Liste l Elemente vom Typ T T ist eine Instanz der Typklasse Ord Ausgabe: sortierte Liste r length l = length r für alle x ∈ l: length [ a | a <- l, a == x] = length [ b | b <- r, b == x] is_ordered l Giegerich Guards Arrays Primzahlen Sortieren Insertion Sort Quicksort Insertion Sort Universität Bielefeld Programmieren in Haskell Giegerich Guards 1 2 3 > isort :: Ord a = > [ a ] -> [ a ] > isort [] = [] > isort ( x : xs ) = insert x ( isort xs ) where Arrays Primzahlen Sortieren Insertion Sort 4 Quicksort 5 6 7 8 > > > > insert x insert x | x <= | x > [] = [ x ] ( y : ys ) y = x : y : ys y = y :( insert x ys ) Quicksort Universität Bielefeld Programmieren in Haskell Giegerich Quicksort – noch bekannt aus der ersten Vorlesung ...? Guards Arrays Primzahlen Sortieren 1 2 3 4 5 > qsort :: Ord a = > [ a ] -> [ a ] > qsort [] = [] > qsort ( a : xs ) = qsort [ b | b <- xs , b < a ] ++ > [ a ] ++ > qsort [ b | b <- xs , b >= a ] Insertion Sort Quicksort Zum Ende noch ein Rätsel 69 Unsere Programm enthält die Definition > xx = round 4.5 Wie berechnen xx zweimal und betrachten die Rechenschritte: 3 4 5 Arr > : s + s Arr > xx 4 (425 reductions , 844 cells ) 6 7 8 9 Programmieren in Haskell Giegerich Guards Arrays 1 2 Universität Bielefeld Arr > xx 4 (20 reductions , 30 cells ) Wieso ist die zweite Berechnung schneller? Was ist überhaupt eine “Reduktion”? Primzahlen Sortieren Insertion Sort Quicksort