Programmieren in Haskell

Werbung
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
Herunterladen