Technische Universität Darmstadt Telecooperation/RBG Grundlagen der Informatik 1 Thema 10: Zuweisungen und andere Effekte Dr. Guido Rößling Copyrighted material; for TUD student use only Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Zuweisungen und andere Effekte: Übersicht • • • • • • • • Funktionen mit Gedächtnis, Version 2 set! Beispiel: Implementierung eines Addressbuchs Wie Zuweisungen unserer Programmiermodell ändern Sequenzierung von Ausdrücken mit(begin ..) Teilen, Äquivalenz und Identität Nutzen von Zustandsvariablen zur Kommunikation Zuweisungen und Modellierung, Performanz, Streams Grundlagen der Informatik I: T10 2 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Wichtige Eigenschaften von Funktionen • Egal, wie oft wir eine Funktion mit ein und derselben Eingabe benutzen, wir bekommen immer das gleiche Ergebnis • An jeder Stelle eines Programms können wir einen Funktionsaufruf durch seine Definition oder seinen Wert ersetzen, ohne den Sinn des Programms zu verändern – Wir können – Wir können durch 8 ersetzen succ (list 3 5)) durch (+ 3 5) (map ((lambda (f a-list) … ) succ (list 3 5)) Definition von map ist – Wir können (map succ (list 3 5)) durch ersetzen, wobei … die (list 4 6) Grundlagen der Informatik I: T10 ersetzen 3 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Funktionen mit Gedächtnis • Diese Eigenschaft wird manchmal Gleiches durch Gleiches ersetzen oder referentielle Transparenz genannt, dieser Programmierstil als rein funktional bezeichnet • Die Eigenschaft folgt direkt aus der Definition des Substitutionsmodells Die Bedeutung einer Funktion wird vollkommen durch ihre Eingabe/Ausgabe - Relation charakterisiert • Manchmal ist es jedoch praktischer, Funktionen mit Gedächtnis einsetzen zu können • Beispiel: Implementieren eines Zählers – Wir wollen zum Beispiel zählen, wie oft die “subst”-Funktion während der Evaluation eines Programms in unserem Interpreter aufgerufen wird Grundlagen der Informatik I: T10 4 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Funktionen mit Gedächtnis • “Rein funktionaler” Zähler: (define (count old-count) (+ old-count 1)) • Problem: – Wie müssen die eval-Funktion modifizieren • Sie muss die aktuelle Zählung als zusätzliche Eingabe annehmen, muss die neue Zählung als zusätzliche Ausgabe liefern – Wir müssen alle Funktionen modifizieren, die eval aufrufen • eval-if, eval-app, run-program, … • Sie alle müssen einen zusätzlichen Eingabe/Ausgabe - Parameter annehmen/liefern und die entsprechenden Zähler-Werte aller evalAufrufe zu ihrem eigenen Ausgabewert des Zählers hinzufügen – Diese Lösung ist in höchstem Maße unmodular! Grundlagen der Informatik I: T10 5 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Funktionen mit Gedächtnis • Was wir im Beispiel gerne hätten, ist eine Funktion mit Gedächtnis – Sie merkt sich den alten Wert des Zählers. – Wenn sie aufgerufen wird, löst sie einen Nebeneffekt (oder einfach einen Effekt) aus: sie erhöht den Wert des Zählers • Dies ist ein Nebeneffekt, weil es nicht Teil der Eingabe/ Ausgabe–Relation ist Grundlagen der Informatik I: T10 6 Zuweisungen Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © • Ein set!-Ausdruck, auch bekannt als Zuweisung, hat folgende Form: – (set! var exp) • Er besteht aus – Einer Variablen, der linken Seite – Einem Ausdruck, genannt rechte Seite. – Die linke Seite eines set!-Ausdrucks ist eine Konstante. – In dieser Veranstaltung verwenden wir nur Variablen, die entweder auf höchster Ebene oder in einem local-Ausdruck definiert sind. • Der Wert eines set!-Ausdrucks ist undefiniert (wie bei einem defineAusdruck) und irrelevant. • Wichtig ist der Effekt beim Auswerten eines set!-Ausdrucks: Nach der Zuweisung werden alle Referenzen auf var zum Wert von exp evaluieren • Wechseln Sie zur Sprache “Advanced Student” / „Fortgeschritten“ in DrRacket, um set! benutzen zu können. Grundlagen der Informatik I: T10 7 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Zuweisungen • Mit Zuweisungen könnte das Zähler-Beispiel wie folgt gelöst werden: ;; provide initial value for counter (define counter-value 0) ;; incrementing counter (define (increment-counter) (set! counter-value (succ counter-value)) • Was bedeutet (define (increment-counter) …)? – Es bedeutet (define increment-counter (lambda () …) – Es ist eine Funktion ohne Parameter! • Wird aufgerufen mit (increment-counter) • Verwechseln Sie nicht increment-counter und (incrementcounter) – Eine Funktion ohne Parameter wäre (beinahe) nutzlos, wenn sie nur rein funktional / effektfrei ist: Sie wäre lediglich eine Konstante • “Beinahe”, weil diese Technik auch dafür benutzt werden kann, um Evaluation zu verhindern Grundlagen der Informatik I: T10 8 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Zuweisungen Grundlagen der Informatik I: T10 9 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Funktionen mit Gedächtnis • Ein anderes Beispiel: Führen eines Telefonbuchs • Telefonbuch-Software leitest zumindest zwei Dienste: – Ein Dienst zum Nachschlagen der Telefonnummer einer Person – Ein Dienst zum Hinzufügen eines Namens und einer Telefonnummer zum Adressbuch • Mögliche Oberfläche für die Software: Grundlagen der Informatik I: T10 10 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Funktionen mit Gedächtnis Der zugehörige Code könnte folgendermaßen aussehen: ;; lookup : symbol -> number or false ;; to lookup the number associated with name in ADDRESS-BOOK ;; if it doesn't find name, the function produces false (define (lookup name) ...) ;; add-to-address-book : symbol number -> void ;; to add name and number to address-book (define (add-to-address-book name number) ...) (define ADDRESS-BOOK (list (list 'Adam 1) (list 'Eve 2))) Grundlagen der Informatik I: T10 11 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Funktionen mit Gedächtnis • Stellen Sie sich nun folgende Interaktion mit DrRacket vor: > (lookup 'Adam) 1 > (lookup 'Dawn) false > (add-to-address-book 'Dawn 4) > (lookup 'Dawn) 4 • Es ist unmöglich, dies mit effektfreien Funktionen zu erreichen! – In einem effektfreien Programm geben Funktionen immer das gleiche Ergebnis für die gleichen Parameter zurück • Ohne Zuweisungen müsste add-to-address das alte Adressbuch verarbeiten und dann ein neues erzeugen, das bei zukünftigen Aufrufen von lookup weiter benutzt werden könnte. Grundlagen der Informatik I: T10 12 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Funktionen mit Gedächtnis • Lösung mit Zuweisungen (define (add-to-address-book! name number) (set! ADDRESS-BOOK (cons (list name number) ADDRESS-BOOK))) • Die Verwendung von “!” in allen Funktionen, die Zuweisungen gebrauchen, ist eine sinnvolle Konvention, wird aber nicht vom Interpreter erzwungen • Wir nennen ADDRESS-BOOK eine Zustandsvariable Grundlagen der Informatik I: T10 13 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Wie sich unser Programmier-Modell ändert • Zuweisungen sind eine fundamentale Änderung unseres Programmiermodells! • Wichtige Invarianten (referentielle Transparenz, Konfluenz), an die wir uns gewöhnt haben, gelten nicht mehr • Plötzlich wird die Zeit zum entscheidender Faktor! – Der Zeitpunkt vor einer Zuweisung im Vergleich zum Zeitpunkt nach einer Zuweisung – Auswertungsreihenfolge wird entscheidend (keine Konfluenz mehr gegeben) • Plötzlich haben wir den Begriff der Identität! – Wir können zwei Adressbücher AB1 und AB2 haben, welche zu einem Zeitpunkt t1 den gleichen Inhalt haben, aber zu einem anderen Zeitpunkt t2 unterschiedlich sind – Das bedeutet, selbst wenn sie den gleichen Inhalt haben, sind es immer noch unterschiedliche Objekte: Sie haben eine Identität, zusätzlich zu ihrem momentanen Wert Grundlagen der Informatik I: T10 14 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Sequenzieren von Auswertungen von Ausdrücken • Angenommen, increment-counter soll nicht nur den aktuellen Wert des Zählers herausgeben, sondern diesen auch erhöhen • Wie kombinieren wir die Ausdrücke der Zuweisung und des ZählerWertes? (define (increment-counter) … … (set! counter-value (succ counter-value)) … counter-value … ) • Wir könnten eine „Kombinations“-Funktion definieren, die zwei Parameter verarbeitet und den ersten ignoriert: (define (combine x y) y) • Weil dieses Muster so gebräuchlich ist, gibt es eine spezielle Form für das Sequenzieren: (begin exp-1 ... exp-n exp) – Wertet exp-1 bis exp-n und exp in gegebener Reihenfolge aus – Liefert den Wert von exp Grundlagen der Informatik I: T10 15 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Ausdrucks-Auswertungen sequenzieren • begin-Ausdrücke verwenden (define (increment-counter) (begin (set! counter-value (succ counter-value)) counter-value)) • Der begin-Ausdruck ist nutzlos, wenn exp-1 … exp-n keine Nebeneffekte verursachen! • Die Auswertungsreihenfolge ist wichtig: – – (define x 3) (begin (set! x (+ x 2)) x) Wenn x vor dem set!-Ausdruck ausgewertet würde, wäre das Ergebnis 3, und nicht 5 Wir können eine Variable nicht länger durch ihren Wert ersetzen (z.B. x durch 3), weil sich ihr Wert ändern kann • Keine referentielle Transparenz • Durch das Ändern einer Definition zerstört eine Zuweisung den momentanen Wert. Wenn der Programmierer die Reihenfolge von Zuweisungen nicht sorgfältig festlegt, kann das fatal sein. Grundlagen der Informatik I: T10 16 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Eingabe/Ausgabe ist eine andere Art von Effekt • Zuweisungen sind nur eine (wichtige) Art von Effekten • Eine andere Art ist die Eingabe und Ausgabe (E/A bzw. I/O) – E/A ist jede Art von Kommunikation mit der „externen“ Welt außerhalb des Programms • Benutzereingaben (Maus, Tastatur, …) • Eingaben von anderen Computern (z.B. per Netzwerk) • Ausgabe (Bildschirm, Drucker, Steuergeräte, …) • Wie die Zuweisungen ist auch E/A kein Teil des rein funktionalen Verhaltens einer Prozedur • Die Reihenfolge von E/A-Effekten ist entscheidend – Etwa die Reihenfolge, in der Seiten gedruckt werden oder in der Motoren einer Maschine an- und ausgeschaltet werden Grundlagen der Informatik I: T10 17 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Eingabe/Ausgabe ist eine andere Art von Effekt • Nehmen Sie das Beispiel der folgenden E/A Funktion – draw-circle etc. • Weil wir bisher nicht viel über Sequenzen und Effekte wussten, „missbrauchten“ wir die and-Funktion, um die Effekte zu sequenzieren – (and (draw-circle (make-posn 50 50) 100 ‘yellow)) (draw-circle (make-posn 30 30) 100 ‘green))) • Durch die Nutzung von begin werden die Effekte besser sequenziert – (begin (draw-circle (make-posn 50 50) 100 ‘yellow)) (draw-circle (make-posn 30 30) 100 ‘green))) Grundlagen der Informatik I: T10 18 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Einige Standard E/A-Funktionen • Ausgabe des Parameters nach stdout – Als Wert nach stdout: print : any -> void – Ohne Hochkomma an Symbolen und Strings etc.: display : any -> void – Traditionelle Art, irgendwo zwischen print und display: write : any -> void – Wie write, aber mit automatischem Zeilenumbruch und Einrückung: pretty-print : any -> void – Formatierung der restlichen Argumente passend zum ersten: printf : string any ... -> void – Ausgabe eines Zeilenumbruchs: newline : -> void • Lesen von Eingaben vom Benutzer: read : -> sexp Beispiel: (begin (printf "Enter your name:") (printf "Hello ~v" (read))) Grundlagen der Informatik I: T10 19 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Ändern von lokalen Werten • Wir können jeden definierten (define) Namen ändern – Nicht nur globale, sondern auch lokale Definitionen • Weil lokale Definitionen einmal pro Aufruf der entsprechenden Prozedur verfügbar sind, haben wir eine dynamische und unbegrenzte Anzahl von Variablen • Beispiel: Zähler mit lokalen Variablen (define (make-counter init) (local ((define counter-value init)) (lambda () (begin (set! counter-value (succ counter-value)) counter-value)))) (define c1 (make-counter 0)) (define c2 (make-counter 0)) Grundlagen der Informatik I: T10 (c1) (c1) (c1) (c2) (c2) à à à à à 1 2 3 1 2 20 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Ändern von lokalen Werten • Die Anzahl von Zählern ist unbegrenzt – Beispiel: 500 Zähler erstellen (define list-of-counters (build-list 500 (lambda (n) (make-counter 0)))) • Das wäre mit globalen Variablen nicht möglich – Die Anzahl von globalen Variablen ist konstant! Grundlagen der Informatik I: T10 21 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Entwerfen von Funktionen mit Gedächtnis • Wie beeinflussen Zuweisungen unseren Entwurfsprozess? • Zustandsvariablen sollten immer eine Zweckbeschreibung (purpose statement) an der Stelle haben, an der sie definiert und initialisiert werden ;; State Variable: ;; address-book : (listof (list symbol number)) ;; to keep track of pairs of names and phone numbers (define address-book empty) • Wenn eine Funktion einen Effekt hat, sollte ihr Effekt beschrieben werden ;; ;; ;; ;; add-to-address-book : symbol number -> void Purpose: the function always produces (void) Effect: to add (list name phone) to the front of address-book Grundlagen der Informatik I: T10 22 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Entwerfen von Funktionen mit Gedächtnis • Das Bereitstellen von Beispielen und Tests wird schwieriger – Die Zeitpunkte müssen mit einbezogen werden ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; Examples: if address-book is empty and we evaluate (add-to-address-book 'Adam 1), address-book is (list (list 'Adam 1)). if address-book is (list (list 'Eve 2)) and we evaluate (add-to-address-book 'Adam 1), address-book is (list (list 'Adam 1) (list 'Eve 2)). if address-book is (list E-1 ... E-2) and we evaluate (add-to-address-book 'Adam 1), address-book is (list (list 'Adam 1) E-1 ... E-2). ;; Tests: (check-expect (begin (set! address-book empty) (add-to-address-book 'Adam 1) address-book) (list (list Adam 1))) Grundlagen der Informatik I: T10 23 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Sind Zustandsvariablen nötig? • Gibt es Programme, die nicht ohne Zuweisungen geschrieben werden können? – Nein! Jedes Programm mit Zuweisungen kann in ein äquivalentes Programm ohne Zuweisungen umgeschrieben werden • Beispiel: ;; lookup : symbol addressbook -> number or false ;; to lookup the number associated with name in ADDRESS-BOOK ;; if it doesn't find name, the function produces false (define (lookup name ab) ...) ;; add-to-address-book : symbol number addressbook -> address-book ;; to add name and number to address-book (define (add-to-address-book name number ab) ...) (lookup 'dawn (add-to-address-book 'dawn 123 empty)) • Die Implementierung für lookup/add-to-address-book kann dieselbe sein wie lookup/add für maps Grundlagen der Informatik I: T10 24 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Sind Zustandsvariablen nötig? • Der Unterschied zwischen den beiden Versionen ist, dass der Aufrufer das Adressbuch kennen und es im Auge behalten muss • Wenn es viele unterschiedliche Aufrufer gibt, die sich dasselbe Adressbuch teilen sollen, kann das schwierig werden • Zustandsvariablen können simuliert werden, indem jede Funktion umgeschrieben wird, um ein Zustandsobjekt als zusätzlichen Parameter zu verarbeiten und ein (möglicherweise unterschiedliches) Zustandsobjekt als zusätzlichen Rückgabewert zurück zu liefern – Das Zustandsobjekt repräsentiert den momentanen „Zustand der Welt“ - die Werte aller Zustandsvariablen – Eine Funktion, die einem Wert etwas zuweisen möchte, kann ein anderes Zustandsobjekt zurückgeben Grundlagen der Informatik I: T10 25 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Sind Zustandsvariablen nötig? • Das Zustandsobjekt wird dann durch das Programm gereicht • Zum Beispiel könnte (f (g 42) (h 23)) wie folgt sein für (define-struct result (value state) (local ((define r1 (g 42 current-state)) (define r2 (h 23 (result-state r1)))) (f (result-value r1) (result-value r2) (result-state r2))) • Beachten Sie, wie dieses Programm eine Auswertungsreihenfolge von links nach rechts etabliert! • Diese Simulation ist sehr raffiniert, weil sie für jedes Programm mit Zustand funktioniert • Für die meisten speziellen Programme ist es weitaus weniger schmerzvoll, Zustandsvariablen loszuwerden Grundlagen der Informatik I: T10 26 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Zustandsvariablen als Kommunikationskanäle • Variablen ermöglichen eine neue Möglichkeit der Kommunikation in Programmen • Ohne Zustandsvariablen erfolgt jegliche Kommunikation durch Prozedur-Parameter und deren Ergebnisse • Mit Zustandsvariablen können verschiedene Programmteile, die auf eine gemeinsam benutze Zustandsvariable zugreifen, Informationen durch die Variable austauschen! – Eine Zustandsvariable ist ein Kommunikationskanal! Grundlagen der Informatik I: T10 27 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Zustandsvariablen und Black Boxes • Die Idee einer Prozedur als Black-Box ist es, Details über ihre Implementierung zu verbergen – Aufrufende müssen nur ihre Schnittstelle kennen • Mit Zustandsvariablen wird die Schnittstelle komplexer – Es ist schwierig vorauszusagen, welche Variablen geändert werden – Änderungen an Variablen sind im Vertrag der Funktion nicht sichtbar • Benutzen Sie niemals (!!!) globale Zustandsvariablen, um Informationen von einem Funktionsaufruf zur Funktionsdefinition oder umgekehrt zu transferieren • Allgemein: Immer wenn Informationen genau so gut durch Funktionsparameter/Rückgabewerte übergeben werden können, benutzen Sie Funktionsparameter/Rückgabewerte und nicht Zustandsvariablen Grundlagen der Informatik I: T10 28 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Zuweisungen und Performanz • In manchen Fällen kann Performanz ein Grund für Zuweisungen sein • Zuweisungen können sehr effizient auf typischer Hardware implementiert werden (so genannte “von Neumann Architektur”) • Das Ändern eines großen zusammengesetzten Werts (z.B. großer Baum/lange Liste) kann sehr teuer sein, wenn er in einem rein funktionalen Stil geändert wird • Auf der anderen Seite kann so ein zusammengesetzter Wert normalerweise auf eine nicht-funktionale (destruktive) Art in konstanter Zeit modifiziert werden Grundlagen der Informatik I: T10 29 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Zuweisungen und Streams • Streams sind manchmal eine gute Alternative zu Zuweisungen – Wir modellieren das zeitabhängige Verhalten durch einen Stream • Beispiel: Zufallszahlen – Wir haben eine initiale Zufallszahl random-init – Wir haben eine Funktion, die aus der vorherigen Zufallszahl die nächste Zufallszahl berechnet: rand-update Version mit einem Stream (define random-numbers (my-cons random-init (map rand-update random-numbers))) Version mit Zuweisung (define rand (local ((define x random-init)) (lambda () (begin (set! x (rand-update x)) x)))) Grundlagen der Informatik I: T10 30 Zustandsvariablen: The Good, the Bad and the Ugly Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © • Zustandsvariablen bevorzugen Freiheit von Kommunikation zu Lasten von Sicherheit und Vorhersehbarkeit • Zuweisungen sind nicht per se gut oder schlecht (oder häßlich) – Sie können ein extrem mächtiges Werkzeug sein • Modularität und Performanz – Aber sie machen das Programm auch weniger verständlich und vorhersehbar • Keine Konfluenz, keine referentielle Transparenz, implizit verborgene (möglicherweise unbeabsichtigte) Kommunikation, … • Sie sollten sich dieser impliziten Kosten der Benutzung von Zuweisungen bewusst sein! • Merken Sie sich, dass jedes Programm ohne Zuweisungen geschrieben werden kann • Eine gute Daumenregel ist, dass Sie viel weniger Zuweisungen benötigen, als Sie denken! Grundlagen der Informatik I: T10 31 Dr. G. Rößling Prof. Dr. M. Mühlhäuser RBG / Telekooperation © Zustandsvariablen: The Good, the Bad and the Ugly • Eine Warnung an jene, die bereits Erfahrungen mit irgendeiner Programmiersprache gemacht haben: – Verfallen Sie nicht zurück in alte (schlechte) Angewohnheiten! – Fühlen Sie sich nicht zu sicher, weil Sie nun die Mechanismen kennen, die Sie immer angewendet haben. – Betrachten Sie Berechnungen nicht hauptsächlich als Abfolge von Berechnungs- und Zuweisungsschritten! • Das skaliert nicht für große Programme • Denken Sie eher in Kategorien der Problemzerlegung und Problemkomposition • Zuweisungen werden in dieser Vorlesung u. a. deshalb erst so spät eingeführt, damit Sie diese Denkweise ablegen. Grundlagen der Informatik I: T10 32