Grundlagen der Programmierung 2 (1.D)

Werbung
Grundlagen der Programmierung 2
(1.D)
Prof. Dr. Manfred Schmidt-Schauÿ
Künstliche Intelligenz und Softwaretechnologie
8. Mai 2007
Fallunterscheidung if-then-else
Die Definition
mein_if
x y z
= case x of True -> y; False -> z
ist äquivalent zum if . then . else
Grundlagen der Programmierung 2 (1.D)
- 1 -
Reduktionsregel zum case
case-Reduktion
(case (c t1 . . . tn) of . . . (c x1 . . . xn → s) . . .)
s[t1/x1, . . . , tn/xn]
(mit sharing)
(case (c t1 . . . tn) of . . . (c x1 . . . xn → s) . . .)
let {x1 = t1; . . . ; xn = tn} in s
Grundlagen der Programmierung 2 (1.D)
- 2 -
Funktionsdefinitionen: Strategie
Fallunterscheidungen mit Mustern in Funktionsdefinitionen:
können als case-Ausdrücke geschrieben werden.
Haskell erlaubt überlappende Muster
Auswertungs-Strategie in
Funktionsdefinitionen mit Mustern und Wächtern:
Muster von oben nach unten,
bis Muster passt und Wächter = True
Es gilt:
Diese Strategie ist mittels geschachtelter case-Ausdrücke
definierbar
Grundlagen der Programmierung 2 (1.D)
- 3 -
Auswertung in Haskell
Kombination von Transformation und Auswertung:
Haskell- Programm
Entzuckerung
Programm in Kernsprache
Syntaxanalyse
Syntaxbaum des Programms
Auswertung
(operationelle Semantik)
transformierter Syntaxbaum
Grundlagen der Programmierung 2 (1.D)
- 4 -
Entzuckerung: Beispiel
map f []
map f (x:xs)
= []
= f x : map f xs
kann man transformieren zu:
map f lst
= (case lst of [] -> []; (x:xs) -> f x : map f xs)
(Nach einer Programmanalyse)
Grundlagen der Programmierung 2 (1.D)
- 5 -
Bemerkungen zu: Normaler Reihenfolge der
Auswertung
•
Normale Reihenfolge der Auswertung ist erweitert
um neue Reduktion:
case-Reduktion
•
Normale Reihenfolge der Auswertung reduziert
nicht unter Konstruktoren (auch nicht in Abstraktionen)
Dadurch kann man (potentiell) unendliche Listen verarbeiten
Grundlagen der Programmierung 2 (1.D)
- 6 -
Listenausdrücke,
Listen-Komprehensionen;
(list comprehensions)
Analog zu Mengenausdrücken, aber Reihenfolge der Listenelemente im
Resultat wird festgelegt.
Beispiele:
[x
|
[f x |
[x
|
[(x,y)
x
x
x
|
<- xs]
<- xs]
<- xs, p x]
x <- xs, y <-ys]
[y | x <- xs, y <-x]
entspricht
entspricht
entspricht
entspricht
entspricht
xs
map f xs
filter p xs
xs "kreuzprodukt" ys
fuer endliche Listen
concat
Syntax:
[hAusdrucki ”|” hGeneratori | hFilteri{, {hGeneratori | hFilteri}}∗].
Grundlagen der Programmierung 2 (1.D)
- 7 -
Listen-Komprehensionen
[Resultatausdruck | Generatoren,Testfunktionen]
Generator:
Prädikat:
v <- liste
Test auf True/False.
Wirkungsweise:
generiert Elemente der Liste
Element wird akzeptiert/nicht akz.
die Generatoren liefern nach und nach die Elemente der
Listen.
Wenn alle Prädikate zutreffen, wird Resultatausdruck in
die Resultatliste aufgenommen.
neue lokale Variablen sind möglich
deren Geltungsbereich ist rechts von der Einführung
Grundlagen der Programmierung 2 (1.D)
- 8 -
Listen-Komprehensionen: Beispiel
[(x,y) | x <- [1..10], even x, y <- [2..6], x < y]
Resultat: [(2,3),(2,4),(2,5),(2,6),(4,5),(4,6)]
Begründung: Erzeugungsreihenfolge:
x
y
?
1
N
2
2
N
2
3
Y
2
4
Y
2
5
Y
Grundlagen der Programmierung 2 (1.D)
2
6
Y
3
N
4
2
N
4
3
N
4
4
N
4
5
Y
4
6
Y
5
N
6
2
N
...
...
...
- 9 -
Listen-Komprehensionen: Beispiel
[(x,y) | x <- [1..10], y <- [1..x]]
[(1,1),
(2,1),(2,2),
(3,1),(3,2),(3,3),
(4,1),(4,2),(4,3),(4,4),
(5,1),(5,2),(5,3),(5,4),(5,5),
(6,1),(6,2),(6,3),(6,4),(6,5),(6,6),
(7,1),(7,2),(7,3),(7,4),(7,5),(7,6),(7,7),
(8,1),(8,2),(8,3),(8,4),(8,5),(8,6),(8,7),(8,8),
(9,1),(9,2),(9,3),(9,4),(9,5),(9,6),(9,7),(9,8),(9,9),
(10,1),(10,2),(10,3),(10,4),(10,5),(10,6),(10,7),(10,8),(10,9),(10,10)]
Grundlagen der Programmierung 2 (1.D)
- 10 -
Listen-Komprehensionen: Beispiel
Liste der nicht durch 2,3,5 teilbaren Zahlen. Die erste Nicht-Primzahl
darin ist 49:
> [x | x <- [2..], x ‘rem‘ 2 /= 0, x ‘rem‘ 3 /= 0, x ‘rem‘ 5 /= 0]
[7,11,13,17,19,23,29,31,37,41,43,47,49,53,59,61,67,71,73,77,79,83,89,91,..
Grundlagen der Programmierung 2 (1.D)
- 11 -
Listen-Komprehensionen:
Beispiel Primzahlen
primes = 2: [x | x <- [3,5..],
and (map (\t-> x ‘mod‘ t /= 0)
(takeWhile (\y -> y^2 <= x) primes))]
Grundlagen der Programmierung 2 (1.D)
- 12 -
Optimierungen: am Beispiel foldl
foldl
:: (a -> b -> a) -> a -> [b] -> a
foldl f z []
= z
foldl f z (x:xs) = foldl f (f z x) xs
foldl’
:: (a -> b -> a) -> a -> [b] -> a
foldl’ f a []
= a
foldl’ f a (x:xs) = ((foldl’ f) $! (f a x)) xs
fun $! x
entspricht:
Grundlagen der Programmierung 2 (1.D)
Zuerst x auswerten, dann fun x
- 13 -
Ressourcenbedarf von foldl, foldr, foldl’
foldl und foldr
haben unterschiedlichen Ressourcenbedarf
in Abhängigkeit vom Operator
foldl’ ist speicher-optimiertes foldl
(abh. von Operator)
foldr (++) [] xs ist schneller als foldl (++) [] xs
foldl’ (+) 0 xs braucht weniger Platz als foldr (+) 0 xs
Main> length (foldr (++) [] [[x] | x <- [1..1000]])
1000 :: Int
(29036 reductions, 43060 cells)
Main> length (foldl (++) [] [[x] | x <- [1..1000]])
1000 :: Int
(527532 reductions, 1539550 cells, 6 garbage collections)
Grundlagen der Programmierung 2 (1.D)
- 14 -
Weitere Listen-Funktionen
Umdrehen einer Liste:
reverse_naiv []
reverse_naiv (x:xs)
= []
= (reverse_naiv xs) ++ [x]
effizientes Umdrehen einer Liste (iterativer Prozess)
reverse xs = foldl (\x y -> y:x) [] xs
Grundlagen der Programmierung 2 (1.D)
- 15 -
Weitere Listen-Funktionen
-- Restliste nach n-tem Element
drop 0 xs
= xs
drop _ []
= []
drop n (_:xs) | n>0 = drop (n-1) xs
drop _ _
= error "Prelude.drop: negative argument"
-- Bildet Liste der
zip
::
zip (a:as) (b:bs)
zip _ _
Paare
[a] -> [b] -> [(a,b)]
= (a,b) : zip as bs
= []
--- aus Liste von Paaren ein Paar von Listen
unzip :: [(a,b)] -> ([a],[b])
unzip
= foldr (\(a,b) (as,bs) -> (a:as, b:bs)) ([], [])
Grundlagen der Programmierung 2 (1.D)
- 16 -
Beispiele
drop 10 [1..100]
----->
zip "abcde" [1..] ----->
[11,12,...
[(’a’,1),(’b’,2),(’c’,3),(’d’,4),(’e’,5)]
unzip (zip "abcdefg" [1..]) ----> ("abcdefg",[1,2,3,4,5,6,7])
Grundlagen der Programmierung 2 (1.D)
- 17 -
Ressourcenbedarf von Listenfunktionen
Drei Möglichkeiten der Auswertung:
Komplett-Auswertung z.B. beim Drucken der Ergebnisliste im Interpreter
Rückgrat-Auswertung nur soviel, wie length von der Ergebnisliste
benötigt.
Kopf-Auswertung (einfache. Ausw.) nur soviel, dass die Frage
Ist die Ergebnisliste leer oder nicht leer“ beantwortet werden kann.
”
Grundlagen der Programmierung 2 (1.D)
- 18 -
Ressourcenbedarf von Append
xs,ys seien ausgewertete Listen der Länge m und n.
Bei Rückgrat-Auswertung:
xs ++ ys
benötigt O(m + n) Reduktionsschritte
O(m) Platz: nur das Rückgrat muss kopiert werden
Grundlagen der Programmierung 2 (1.D)
- 19 -
Ressourcenbedarf von map
xs sei ausgewertete Liste der Länge n
map f xs:
Rückgratauswertung:
Komplett-Auswertung:
O(n) Reduktionsschritte
# Reduktionsschritte abhängig von f
Rückgratauswertung:
Komplett-Auswertung:
O(n) Speicherbedarf
Speicherbedarf abhängig von f
Grundlagen der Programmierung 2 (1.D)
- 20 -
Ressourcenbedarf von concat
Sei xs ausgewertete Liste von Listen der Länge m,
das i-te Element sei eine Liste mit ni Elementen.
concat xs bei Rückgratauswertung
Reduktionsschritte:
n1 + n2 + . . . nm + m
Platzbedarf:
O(n1 + . . . + nm−1 + m)
letzte Liste muss nicht kopiert werden
Grundlagen der Programmierung 2 (1.D)
- 21 -
Herunterladen