Auswertungsregeln für Listenfunktionen in Python: operationale
Transcription
Auswertungsregeln für Listenfunktionen in Python: operationale
Auswertungsregeln für Listenfunktionen in Python: operationale Semantik Operationale Semantik einer imperativen Programmiersprache mit Listen; hier Python Modellierung der Verzeigerung zwischen Objekten mittels Variablennamen als Ersatz für Adressen. Modellierung des Speichers als abstrakte Schnittstelle Dazu: Heap (Halde) und Heap-Variablen P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 1 Datenstruktur Heap, Halde Definition Eine Halde (Heap) besteht aus: • • Menge von Heap-Variablen: VH Menge von HH-Bindungen an Heapvariablen Möglichkeiten sind: x 7→ v: v ist Basiswert (Zahl, Character oder Nil) x 7→ ⊥, x 7→ None x 7→ [x1|x2]: Wobei [x1|x2] Paar (Box) aus zwei Heap-Variablen ist Zusatzbedingungen • Einige Heap-Variablen sind als Wurzel-Variablen markiert. • pro Heap-Variable ist nur eine HH-Bindung im Heap • Heap-Variablen, die in einer Box vorkommen, müssen eine HH-Bindung im Heap haben. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 2 Datenstruktur Heap, Halde Die beiden Formen der HH-Bindung x x v P raktische Inf ormatik x1 1, W S 2004/05, F olien P ython−4, x2 (14. Januar2005) Seite 3 Heapvariablen Eine Heapvariable x repräsentiert einen Basiswert oder ein komplexes Heap-Objekt: einen gerichteten Graphen ausgehend von x. Beispiel Wenn H = {x 7→ [x1|x2], x1 7→ 1, x2 7→ Nil)}, dann repräsentiert x die Liste [1] x x1 x2 Nil 1 Der Programmvariablen z: wird mittels {z 7→ x} ein Objekt zugeordnet wobei x Heapvariable ist, die das Objekt repräsentiert. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 4 Umgebungen mit Heaps Gegeben ein Heap (Halde) H x 7→ y R (PH-) Bindung: Umgebungsrahmen: (R1, R2, H) x Programm- und y Heap-Variable R Menge von PH-Bindungen zum Heap H R1 ist lokaler Umgebungsrahmen, Umgebung (Variablenumgebung, R2 ist globaler Umgebungsrahmen; Zustand) H ist Halde . Annahme Wurzel-Variablen von H sind mindestens die Heap-Variablen, die in den Variablenumgebungen R1, R2 als Ziele vorkommen. Beachte In Implementierungen: Umgebung ist als Stack organisiert. Zur Optimierung werden Werte auf einem Auswertungsstack abgelegt und verarbeitet. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 5 Beispiele Darstellung der Liste [100] mit Umgebung und Halde: PH-Bindung: Halde z1 z1 7→ z2 {z2 7→ [z3|z4], z3 7→ 100, z4 7→ Nil} z2 P raktische Inf ormatik 1, W S z3 z4 100 Nil 2004/05, F olien P ython−4, (14. Januar2005) Seite 6 Beispiele Darstellung einer zyklischen Liste Druckbild: [[[[[...]]]]]] AB AA Nil Die Halde dazu sieht so aus: {AA 7→ [AA|AB], AB 7→ Nil} P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 7 Erreichbarkeitsrelation H,∗ Erreichbarkeitsrelation −−→ des Heaps H: H,∗ x −−→ y, wenn • • x = y, oder ∃ x1, . . . , xn, so dass x = x1 und y = xn und für alle i = 1, . . . n − 1 entweder xi 7→ [xi+1|x0i+1] ∈ H oder xi 7→ [x0i+1|xi+1] ∈ H Heap-Variable x ist erreichbar (aktiv), H,∗ wenn sie von einer Wurzelvariable x0 aus erreichbar, d.h. : x0 −−→ x, andernfalls ist x unerreichbar (redundant). H,∗ Alternativ: −−→ ist die reflexiv-transitive Hülle der Relation: x → y, wenn x 7→ [y|y 0] ∈ H oder x 7→ [y 00|y] ∈ H P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 8 Speicherbereinigung, garbage collection HH-Bindungen von unerreichbaren Variablen kann man aus dem Heap entfernen. Das ist die Aufgabe des Garbage-Collectors Als Wurzelvariablen nimmt man sinnvollerweise: • • Heap-Variablen, auf die vom Stack aus referenziert wird Heap-Variablen, auf die das Programm referenziert. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 9 Regeln der (erweiterten) operationalen Semantik zweite Variante der operationalen Semantik von Python, die auch zyklische Listen beschreiben kann. Notationen [[.]]: (Z, s) →e (Z 0, x) P raktische Inf ormatik 1, W S innen syntaktische Konstrukte, außen: Aktionen oder Werte. Werte s im Zustand Z aus, Resultat ist ein Objekt, repräsentiert durch die Heapvariable x; Z 0 ist der neue Zustand 2004/05, F olien P ython−4, (14. Januar2005) Seite 10 Auswertung eines Listenausdrucks Python-Listenausdruck: [a1, . . . , an]; Halde H (n ≥ 1): Die Regel beschreibt einen Schritt der Auswertung: (Z; [[a1]]) →e (Z1; x1) (Z1; [[[a2, . . . , an]]]) →e (Z2; x2) Z2 = (R2,1, R2,2, H2) (Z; [[[a1, . . . , an]]]) →e (R2,1, R2,2, H2 ∪ {x 7→ [x1|x2]}); x Am Ende der Listen verwenden wir die spezielle Konstante Nil: ((R1, R2, H); [[[]]]) →e ((R1, R2, H ∪ {x 7→ Nil}); x) P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 11 Die Alias-Namens-Problematik: Beispiel a = [1,2] b = a Nach a = [1,2]: Bindungen: Heap: . . . , a 7→ u1, . . . {u1 7→ [u2|u3], u2 7→ 1, u3 7→ [u4|u5], u4 7→ 2, u5 7→ Nil, . . .} Nach b = a Bindungen: Heap: . . . , a 7→ u1, . . . , b 7→ u1, . . . {u1 7→ [u2|u3], u2 7→ 1, u3 7→ [u4|u5], u4 7→ 2, u5 7→ Nil, . . .} a u1 b u2 u3 1, W S u5 Nil 2 1 P raktische Inf ormatik u4 2004/05, F olien P ython−4, (14. Januar2005) Seite 12 Auswertung von append Auswertung von a.append(b) wobei a, b Variablen. ((R1, R2, H); [[a.append(b)]]) →e ((R1, R2, H 0); None) Bedingungen: vor der Auswertung nach der Auswertung H = H0 ∪ {x 7→ Nil} x letzte Heap-Variable zu a mit x 7→ Nil b 7→ x1 ist in R1 H 0 = H0 ∪ {x 7→ [x1|x2], x2 7→ Nil} x2 ist neu erzeugte Heap-Variable a, b sind auch die Namen der Programm-Variablen Effekt: {x 7→ Nil} wurde ersetzt durch {x 7→ [x1 | x2]} P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 13 Auswertung von append Veranschaulichung der Auswertung von a.append(b): Vorher: a u1 b x x1 Nil w Nachher: a u1 b x x1 x1 x2 Nil w Dadurch wird (die Liste) b als letztes Element an die Liste angehängt. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 14 Beispiel zu append Zyklische Struktur mittels append-Anwendungen Liste mit unendlicher Verschachtelungstiefe: Nach a = [1, 2]; a.append(a) Halde: {u1 7→ [u2|u3], u2 7→ 1, u3 7→ [u4|u5], u4 7→ 2, u5 7→ [u1|u7], u7 7→ Nil, . . .} a u1 u2 u3 1 u4 u5 u1 u7 Nil 2 Beachte: u1 tritt zweimal auf, muss aber das gleiche Ziel haben. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 15 operationelle Semantik von insert Fall: insert innerhalb der Liste ((R1, R2, H); [[a.insert(i, b)]]) →e ((R1, R2, H 0); None) Hierbei muss gelten: H 0 = H0 ∪ {xi 7→ [y1|y2], y2 7→ [ei+1|xi+1]}, wobei xi Heap-Variable zum i-ten Listen-Tail H = H0 ∪ {xi 7→ [ei+1|xi+1]}, b 7→ y1 ∈ R1, und y2 neue Heap-Variable. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 16 operationelle Semantik von insert (2) vorher: ei xi ei+1 ... xi+1 nach insert: ei xi ei+1 y1 P raktische Inf ormatik 1, W S ... xi+1 y2 2004/05, F olien P ython−4, (14. Januar2005) Seite 17 operationelle Semantik von insert (3) Fall: insert am Ende der Liste ((R1, R2, H); [[a.insert(i, b)]]) →e ((R1, R2, H 0); None) Hierbei muss gelten: H 0 = H0 ∪ {xi 7→ [y1|y2], y2 7→ Nil}, wobei i ≥ len(a) H = H0 ∪ {xi 7→ Nil}, b 7→ y1 ∈ S, y2 neue Heap-Variable. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 18 operationelle Semantik von map operationelle Semantik von map: aus kleineren Operationen zusammensetzen: Zum Beispiel als operationale Semantik der Funktion: def map_demo(f,xs): if len(xs) == 0: return []; else: wert = map_demo(f, xs[1:len(xs)]); wert.insert(0,f(xs[0])); ## Seiteneffekt (wie cons) return wert; P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 19 operationelle Semantik von a[i] = b ((R1, R2, H); [[a[i] = b]]) → (R1, R2, H 0) mit H 0 = H0 ∪ {xi 7→ [z|y2]} wobei H = H0 ∪ {xi 7→ [y1|y2]}, xi Heap-Variable zum i-ten Listen-Tail, und b 7→ z ∈ R1 P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 20 Indirektionen Indirektionen sind Bindungen x1 7→ x2, wobei x1, x2 Heap-Variablen sind. (Gibt es in Python nicht.) Objekt zu x1: im Heap ist das Objekt zu x2 bzw. Suche über weitere Indirektionen Diese Indirektionen sind nicht sichtbar in der Programmiersprache • • • • leicht komplizierteres formales Modell Indirektionszyklen sind möglich Verwendung in Implementierungen z.T. wegen der Effizienz Notwendig ist dann das Verkürzen von Indirektionsketten P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 21 Implementierung der Halde in einem Speichermodell Speichermodell RAM-Speicher (Random Access Memory) S ist endliche Folge der Länge L von Bytes (1 Byte = 8 Bit) indiziert mit 0, . . . , L − 1. L ist die Größe des Speichers. Der gespeicherte Wert unter Adresse i ist das Byte S(i) Adresse: Anfangsindex einer Subfolge (in Binärdarstellung). Die benötigte Adresslänge in Bits ist dlog2(L)e. Zugriffsfunktion get(S, i, l) ergibt Subfolge von S der Länge l ab i. 00101001 0 1 P raktische Inf ormatik 3 2 1, W S 4 5 2004/05, F olien P ython−4, (14. Januar2005) Seite 22 Implementierung der Halde unter Benutzung des RAM-Speichers Wort = Adresse zu 32 Bit Adresse eines Wortes Heapvariable Ein Paar [x1|x2] = = = = P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, Folge von 4 Bytes, im Speicher direkt hintereinander, d.h. S(i), S(i + 1), S(i + 2), S(i + 3). Folge von 4 Bytes erster Index. Wort 2 Worte, die hintereinander sind, d.h. S(i), . . . , S(i + 7). (14. Januar2005) Seite 23 Beispiel zur Haldenimplementierung Betrachte die Bindungen b 7→ x, x 7→ 4, mit 32 Bit-Adressen implementiert mit Programmvariable b, Heapvariable x Wenn b den Wert 100 hat dann ergibt a = get(H, 100, 4) eine Adresse (den Wert von x), und get(H, a, 1) ergibt den Wert 4 200 100 x b 100 P raktische Inf ormatik 4 1, W S 2004/05, F olien P ython−4, 200 (14. Januar2005) Seite 24 Typmarkierungen der Werte In dieser Implementierung fehlen Typmarkierungen der Werte: Indirektionen, Paare, Nil, und Zahlen sind ununterscheidbar. Eine Erweiterung könnte sein: Adresse: Paar: Ganze Zahl: Nil: kein Wert: P raktische Inf ormatik 1, W S 1 1 1 1 1 Byte Byte Byte Byte Byte Markierung Markierung Markierung Markierung Markierung 2004/05, F olien P ython−4, (14. Januar2005) (z.B. (z.B. (z.B. (z.B. (z.B. Seite 25 binär binär binär binär binär 1), und 4 Byte Adresse. 2), und 8 Byte Adressen. 3), und 4 Byte Zahl. 4). 5). Bemerkungen zur Halden-Implementierung Andere Halden-Implementierung sind möglich. • die Adressen eines Adresspaares (einer Box) [x1|x2] müssen nicht hintereinander sein • Adressen brauchen keine aufsteigende Folge von Bytes zu sein • Die Länge der Adressen kann 6= 32 sein. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 26 Felder, Arrays in Python In Python: Listen und Tupel sind gleichzeitig eindimensionale Arrays dürfen heterogen sein. Mehrdimensionale Arrays und Matrizen als Array von Arrays, bzw. mit Listen von Listen Zugriffszeit auf S[i] ist konstant, wenn Index und Array bekannt sind. Handhabung eines Feldes im Programm ist teilweise umständlich: Indexberechnungen sind notwendig explizite oder implizite Größenänderungen eines Arrays verbrauchen Ressourcen. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 27 Beispiele Transposition einer 2-dimensionalen quadratischen Matrix in Python def transpose(a,n): for i in range(n): for j in range(i+1,n): a[i][j], a[j][i] = a[j][i] , a[i][j] return a def testtranspose(n): b = range(n) for i in range(n): b[i] = range(n) for i in range(n): print b[i]; c = transpose(b,n) print " "; print "Nach Transposition:" for i in range(n): print c[i] P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 28 Beispiel: Matrixmultiplikation, Determinante für quadratische Matrizen def matrixMult(a,b): leng = len(a); c = range(leng); for i in range(leng): c[i] = range(leng); for j in range(leng): c[i] [j] = 0; for i in range(leng): for j in range(leng): sum = 0 for k in range(leng): sum = sum + a[i][k]*b[k][j] c[i][j] = sum return c P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 29 Beispiel: Matrixmultiplikation, Determinante def testmatmult(leng): a = range(leng); b = range(leng); print a; print b; for i in range(leng): a[i] = range(leng); b[i] = range(leng); for j in range(leng): a[i] [j] = i; b[i] [j] = j; print a; print b; return (matrixMult(a,b)) P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 30 Beispiel: Determinante def determinante(a): n = len(a); if n <= 0: return 1; else: if n == 1: return a[0][0] else: sum = 0; flip = -1; for i in range(n): flip = (-1)*flip; b = matrixcopy(a); for j in range(n): b[j].pop(0); P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 31 b.pop(i); sum = sum + flip*a[i] [0] * determinante(b); return(sum); Achtung: Laufzeit O(2n), aber es existiert ein O(n3)-Algorithmus Operationale Semantik: Test mit C C: wichtige und verbreitete imperative Programmiersprache für maschinennahes Programmieren, insbesondere Betriebssysteme. C-Compiler ergeben effizienten Code. Abstraktionsgrad nicht so hoch wie Python oder Haskell Wir untersuchen die Eigenschaften der operationalen Semantik von C. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 33 Zitate zu C Aus Kernighan/Ritchie: Programmieren in C: Diese Regeln sind außerordentlich kompliziert, denn sie ” müssen mit einer Mischung von Funktionen alten und neuen Stils fertigwerden. Wenn möglich, sollten Mischungen vermieden werden.“ Die Reihenfolge, in der Argumente bewertet werden, ist nicht ” festgelegt; man beachte, dass sich verschiedene Übersetzer hierin unterscheiden. Die Argumente und Funktionsbezeichner werden jedoch vollständig bewertet [d.h. ausgewertet], mit allen Nebenwirkungen, bevor die Ausführung der Funktion beginnt...“ P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 34 Zitate zu C (2) Ein anderes Zitat zum Versuch einer Andeutung der (funktionalen) operationellen Semantik von C: . . . In anderen Worten, wenn das n-te Kode-Fragment durch ” die Funktion fn repräsentiert wird, und s0 ist der Anfangszustand, dann ist der Endzustand definiert durch sn = fn(fn−1(. . . f1(f0(s0)) . . .)) Die Ausführung . . . Parallelismus.“ P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, der (14. Januar2005) einzelnen Seite 35 Funktionen erlaubt Operationale Semantik eines C-Operators i++ • • ( i Programmvariable) hat aktuellen Wert von i als Return-Wert bewirkt einen Seiteneffekt i := i+1 Die operationale Semantik-Regel zu i++: v = wert(i, Z) P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, w = v + 1 Z 0 = update(i, w, Z) (Z; [[i++]]) →e (Z 0; v) (14. Januar2005) Seite 36 Operationale Semantik des Präfix-Operators ++ Unterschied zwischen ++i und i++: Returnwert von ++i ist (Wert von i)+1. Die operationale Semantik-Regel zu ++i: v = wert(i, Z) w = v + 1 Z 0 = update(i, w, Z) (Z; [[++i]]) →e (Z 0; w) Vergleiche mit: v = wert(i, Z) P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, w = v + 1 Z 0 = update(i, w, Z) (Z; [[i++]]) →e (Z 0; v) (14. Januar2005) Seite 37 Beispiel Betrachte das C-Code-Fragment i = 7; j = i++ * i++ Erwarteter Zustand danach: Die Multiplikation wertet i++ zweimal aus: erstes i++ zweites i++ Der Wert von j ergibt 7, ergibt 8, ergibt j = 7 ∗ 8 = 56. In Java ergibt sich der richtige Zustand P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 38 danach i = 8 danach i = 9. Beispiel: Ausprobieren in C Der C-Code i = 7; j = i++ * i++; kompiliert in gcc ergibt den Zustand j = 49, i = 8 P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 39 Beispiel: Erklärungsversuch Compiler versucht, Doppelauswertung von i++ zu vermeiden und ändert daher das Programm ab: i = 7; j = (let x = i++ in x*x); bzw. in let-freier Schreibweise: i = 7; x = i++; j = x*x; P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 40 Beispiel: Erklärungsversuch (2) Es ergibt sich: Nach Auswertung von x: Danach x ∗ x auswerten {x 7→ 7, i 7→ 8, . . .}. j = 49, Fazit: Vermutlich eine (optimierende) Programmtransformation, die die operationale Semantik in diesem Fall nicht erhält. P raktische Inf ormatik 1, W S 2004/05, F olien P ython−4, (14. Januar2005) Seite 41