Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Grundlagen der Informatik I Thema 5: Abstraktion von Design

Ähnliche Präsentationen


Präsentation zum Thema: "Grundlagen der Informatik I Thema 5: Abstraktion von Design"—  Präsentation transkript:

1 Grundlagen der Informatik I Thema 5: Abstraktion von Design
Prof. Dr. Max Mühlhäuser Dr. Guido Rößling

2 Überblick Ähnlichkeiten in Definitionen Funktionen sind Werte
Abstraktionen aus Beispielen entwerfen Abstraktionen mit Funktionen als Werten entwerfen Funktionen “ad hoc” definieren “pipes-and-filters” Organisation von Berechnungen

3 Ähnlichkeiten in Definitionen
Viele Daten- und Funktionsdefinitionen sehen ähnlich aus: Daten: Symbolliste, Zahlenliste, … Funktionen: Suche nach speziellem Symbol in einer Symbolliste Suche nach spezieller Zahl in einer Zahlenliste Wiederholungen sind die Quelle vieler Programmfehler Der wichtigste Schritt beim Schreiben eines Programms ist Wiederholungen zu eliminieren. Dieser Teil der Vorlesung behandelt folgende Punkte: Ähnlichkeiten in Daten- und Funktionsdefinitionen Designrezepte, um abstrakte Funktion zu entwerfen und Wiederholungen zu vermeiden Die Rolle von Prozeduren höherer Ordnung in diesem Prozess der wichtigste teil des program-modifizierens… Definitions of list-of-numbers and list-of-symbols differ in only two regards: the name of the class of data and the words ``symbol'' and ``number.'‚ Our means of avoiding similarities are specific to Scheme and functional programming languages; still, other languages, in particular object-oriented ones, support similar mechanisms for factoring out similarities -- or (code) patterns as they are somtimes called. CHANGE: schreiben -> editieren von programmen

4 Ähnlichkeiten in Funktionen
Die Benutzung derzeitiger Designrezepte leiten die Schablone einer Funktion (die grundlegende Organisation) aus der Datendefinition der Eingabe ab. Eine Schablone ist eine Methode, um auszudrücken, was wir über die Eingangsdaten wissen. Konsequenz: Funktionen, die die gleiche Art Daten verarbeiten, ähneln sich Wenn Deine Datenstruktur richtig ist, schreibt sich der Rest des Programms von selbst. David Jones CHANGE: Schaplone -> Template?

5 Zwei ähnliche Funktionen
;; contains-doll? : los  ->  boolean ;; to determine whether alos contains ;; the symbol 'doll (define ( contains-doll? alos) (cond [(empty? alos) false] [else [(symbol=? (first alos) 'doll) true] ( contains-doll? (rest alos ))])])) Take a look at the two functions in figure 52, which consume lists of symbols (names of toys) and look for specific toys. The function on the left looks for 'doll, the one on the right for 'car in a list of symbols (los). The two functions are nearly indistinguishable. Each consumes lists of symbols; each function body consists of a cond-expressions with two clauses. Each produces false if the input is empty; each uses a second, nested cond-expression to determine whether the first item is the desired item. The only difference is the symbol that is used in the comparison of the nested cond-expression: contains-doll? uses 'doll and contains-car? uses 'car, of course. To highlight the differences, the two symbols are boxed. Good programmers are too lazy to define several closely related functions. Instead they define a single function that can look for both a 'doll and a 'car in a list of toys.

6 Zwei ähnliche Funktionen
;; contains-car? : los  ->  boolean ;; to determine whether alos contains ;; the symbol 'car (define ( contains-car? alos) (cond [(empty? alos) false] [else [(symbol=? (first alos) 'car ) true] ( contains-car? (rest alos))])]))

7 Eine abstraktere Funktion
Diese abstraktere Funktion konsumiert einen zusätzlichen Parameter: das Symbol, nach dem wir suchen Ansonsten gleicht sie den beiden Originalfunktionen ;; contains? : symbol los -> boolean ;; to determine whether alos contains the symbol s (define (contains? s alos) (cond [(empty? alos) false] [else (cond [(symbol=? (first alos) s) true] [else (contains? s (rest alos))])]))

8 Funktionale Abstraktion
Abstrakte Versionen von Funktionen zu definieren ist sehr nützlich: Eine einzige Funktion kann viele verschiedene Aufgaben erfüllen: contains? kann nach vielen verschiedenen Symbolen suchen, anstatt nur nach einem bestimmten Eine einzige Funktion kann mehrere ähnliche Probleme auf einmal lösen Den Prozess, zwei ähnliche Funktionen zu einer Definition zusammenzuführen, nennt man funktionale Abstraktion

9 Zwei weitere ähnliche Funktionen
less-than ;; less-than: lon number  ->  lon ;; to construct a list of those numbers ;; in alon that are less than t (define (less-than alon t) (cond [(empty? alon) empty] [else [(< (first alon) t) (cons (first alon) (less-than (rest alon) t))] [else (less-than (rest alon) t)])])) alon nraucht nicht sortiert sein!

10 Zwei weitere ähnliche Funktionen
greater-than ;; greater-than: lon number  ->  lon ;; to construct a list of those numbers ;; in alon that are greater than t (define (greater-than alon t) (cond [(empty? alon) empty] [else [(> (first alon) t) (cons (first alon) (greater-than (rest alon) t))] [else (greater-than (rest alon) t)])])) The difference between the two functions is the comparison operator. The left uses <, the right one >. Following the first example, we abstract over the two functions with an additional parameter that stands for the concrete relational operator in below and above:

11 Abstraktion der zwei Funktionen
Der zusätzliche Parameter rel-op von filter1 steht für den konkreten relationalen Operator in less-than und greater-than: (define (filter1 rel-op alon t) (cond [(empty? alon) empty] [else [(rel-op (first alon) t) (cons (first alon) (filter1 rel-op (rest alon) t))] (filter1 rel-op (rest alon) t)])]))

12 Originalfunktionen aus der Abstraktion
Für filter1 müssen wir 3 Argumente bereitstellen: Einen relationalen Operator R, der zwei Zahlen vergleicht Eine Liste L aus Zahlen, und eine Zahl N Die Funktion liefert alle Werte i aus L, für die (R i N) zu true auswertet. Wir können nun less-than und greater-than als „Einzeiler“ mit Hilfe von filter1 erhalten Bei gleicher Eingabe ergeben less-than1 bzw. greater-than 1das gleiche wie less-than bzw. greater-than ;; less-than: lon number  ->  lon (define (less-than1 alon t) (filter1 < alon t)) The calculations show that (filter1 < alon t) computes the same result as (below alon t), which is what we expected. Similar reasoning shows that (filter1 > alon t) produces the same output as (above alon t). ;; greater-than1: lon number  ->  lon (define (greater-than 1alon t) (filter1 > alon t))

13 Wiederverwendung abstrakter Funktionen
Wir können eine abstrakte Funktion auch anders benutzen Das erste Argument von filter1 kann irgendeine Funktion sein, die zwei Zahlen erhält und einen boolean Wert ausgibt Beispiele: (filter1 = alon t) : wählt alle Zahlen n aus alon, für die gilt: n = t (filter1 <= alon t) : Erzeugt eine Liste von Zahlen n aus alon, für die gilt: n<= t. (filter1 >= alon t): Erzeugt die Liste von Zahlen >= t. Wählt die Zahlen aus ( ), deren Quadrat größer 10 ist. (filter1 squared>? (list ) 10) ;; squared>? : number number  ->  boolean (define (squared>? x c) (> (* x x) c))

14 Ähnlichkeiten bei der Datendefinition
Bei ähnlicher Datendefinition sind auch Funktionen ähnlich, die die Elemente dieser Klasse verarbeiten. Eine list-of-numbers ist entweder empty oder (cons n l) n ist eine Zahl l ist eine list-of-numbers Eine list-of-IRs ist entweder empty oder (cons n l) n ist ein IR l ist eine list-of-IRs (define-struct ir (name price)) Ein IR – Inventory Record - ist eine Struktur:  (make-ir n p)  wobei n ein Symbol und p eine Zahl ist. inventory record = inventar eintrag list of numbers ist eine liste von atomaren daten (zahlen) list of Irs ist eine liste von strukturen (define-struct ir)

15 Ähnlichkeiten bei der Datendefinition
;; less-than: number lon  ->  lon ;; to construct a list of those numbers ;; on alon that are less than t (define (less-than alon t) (cond [(empty? alon) empty] [else [(< (first alon) t) (cons (first alon) (less-than (rest alon) t))] [else (less-than (rest alon) t)])])) Take a look at the illustrative example. The function on the left is the function below, which filters numbers from a list of numbers. The one on the right is below-ir, which extracts those inventory records from a list whose prices are below a certain threshold. Except for the name of the function, which is arbitrary, the two definitions differ in only one point: the relational operator.

16 Ähnlichkeiten bei der Datendefinition
;; less-than-ir : number loIR  ->  loIR ;; to construct a list of those records ;; on aloir that contain a price less than t (define (less-than-ir aloir t) (cond [(empty? aloir) empty] [else [(<ir (first aloir) t) (cons (first aloir) (less-than-ir (rest aloir) t))] [else (less-than-ir (rest aloir) t)])])) beachte: <ir geht - aber < mit subskript ir gibt es nicht… ;; <ir : IR number  ->  boolean (define (<ir ir p) (< (ir-price ir) p))

17 Polymorphe Funktionen
Beim Abstrahieren der zwei Funktionen erhalten wir filter1 Wir können less-than-ir mit filter1 definieren: (define (less-than-ir1 aloir t)(filter1 <ir aloir t)) filter1 filtert nicht nur Listen aus Zahlen, sondern auch Listen aus beliebigen Dingen Solange wir eine Funktionen definieren können, die diese beliebigen Dinge mit Zahlen vergleicht Noch abstrakter: Solange wir Funktionen definieren können, die Listenelemente mit Elementen vergleichen, die filter1 als zweites Argument übergeben werden Siehe die folgende Funktion … It should not surprise us to discover yet another use for filter1 -- after all, we already argued that abstraction promotes the reuse of functions for different purposes. CHANGE: see p17 english version for layout-changes top-half

18 Polymorphe Funktionen
;; find : loIR symbol  ->  boolean ;; determines whether aloir contains a record for t (define (find aloir name) (cons? (filter1 eq-ir? aloir name))) ;; eq-ir? : IR symbol  ->  boolean ;; to compare ir's name and p (define (eq-ir? ir p) (symbol=? (ir-name ir) p)) filter1 arbeitet einheitlich mit vielen Arten von Eingabedaten: filter1, angewandt auf eine Liste von X, hat als Ergebnis auch eine Liste von X – egal aus welcher Art von Daten X besteht Polymorphe oder generische Funktionen Of course, filter1 is not the only function that can process arbitrary lists. There are many other functions that process lists independently of what they contain. Here are two functions that determine the length of lists of numbers and IRs: ;; length-lon : lon  ->  number (define (length-lon alon) (cond [(empty? alon) empty] [else (+ (length-lon (rest alon)) 1)]))       ;; length-ir : loIR  ->  number (define (length-ir alon) [else (+ (length-ir (rest alon)) 1)])) The two functions differ only in their names. If we had chosen the same name, say, length, the two definitions would be identical.

19 Parametrische Datendefinitionen
Frage: Wie schreiben wir präzise Verträge für polymorphe Funktionen wie filter1? Antwort: Benutze Datendefinitionen mit Parametern, auch parametrische Datendefinitionen genannt Sie spezifizieren nicht alles über eine Datenklasse Sie benutzen Variablen, um anzudeuten, dass alle Scheme-Daten an einer bestimmten Stelle benutzt werden können TYP VARIABLE Steht für eine beliebige Sammlung von Scheme Daten: Symbole, IRs, etc. Wird ITEM durch einen dieser Namen ersetzt, erhalten wir eine konkrete Instanz dieser abstrakten Datendefinition: Listen von Symbolen, Listen von IRs, etc. Eine Liste von ITEM ist entweder empty oder (cons s l) s ist ein ITEM l ist eine Liste von ITEM  A parametric data definition abstracts from a reference to a particular collection of data in the same manner as a function abstracts from a particular value.

20 Verträge für Polymorphe Funktionen
Beim Definieren von Verträgen besagt die Abkürzung (listof X), dass eine Funktion auf allen Listen funktioniert ;; length : (listof X)  ->  number ;; to compute the length of a list (define (length alox) (cond [(empty? alox) 0] [else (+ (length (rest alox)) 1)])) X ist nur eine Variable  ein Name, der für eine Datenklasse steht Wenn wir length auf ein Element der Klassen (listof symbol) oder (listof IR) anwenden, erhalten wir eine Zahl ?? wie sinnvoll ist die schreibweise (listof X) (listof symnbol)… The function length is an example of simple polymorphism. It works on all classes of lists. While there are other useful examples of simple polymorphic functions, the more common cases require that we define functions like filter1, which consume a parametric form of data and functions that work on this data. This combination is extremely powerful and greatly facilitates the construction and maintenance of software systems. To understand it better, we will next discuss a revision of Scheme's grammar and new ways to write contracts.

21 Überblick Ähnlichkeiten in Definitionen Funktionen sind Werte
Abstraktionen aus Beispielen entwerfen Abstraktionen mit Funktionen als Werten entwerfen Funktionen “ad hoc” definieren “pipes-and-filters” Organisation von Berechnungen

22 Der Wert von Prozeduren
Wir haben bereits die Vorteile der Prozeduren diskutiert: Isolierte Implementierung eines Konzepts: „Separation of Concerns“ statt Wiederholung von Code Wiederverwendung von Code Änderungen an nur einer Stelle, etc. Ermöglicht Rekursion Einheit für „Information Hiding“ Die Prozeduren, die wir bisher verwendet haben, werden Prozeduren erster Ordnung (engl. „first order procedures“) genannt In dieser Vorlesung haben wir eine neue Art von Prozeduren verwendet: Prozeduren höherer Ordnung Prozeduren, die andere Prozeduren als Parameter konsumieren oder als Ergebnisse zurückliefern bisher - nur 1. ordnung jetzt aber auch funktionen als parameter uebergeben !

23 Prozeduren als generelle Methoden
Prozeduren erster Ordnung: Machen Berechnungen unabhängig von den Werten bestimmter Daten (Zahlen/Symbole) usw. Prozeduren höherer Ordnung: Drücken allgemeine Berechnungsmethoden unabhängig von den jeweiligen beteiligten Funktionen aus Werfen wir einen systematischen Blick auf Prozeduren höherer Ordnung Wir müssen zunächst die Definition vom Scheme anpassen Funktionen höherer Ordnung verletzen die bisherige Definition Wählen sie von nun an in DrScheme das „Intermediate Student with Lambda“ Sprachlevel Dann diskutieren wir weiter das Thema „Verträge für polymorphe Funktionen“ prozeduren 1. ordnung: - wir definieren die Prozedur mit Hilfe von Variablen/Parametern und nicht mit speziellen werte (define (square x) (* x x)) - beim aufruf der prozedur uebergeben wir dann die speziellen werte, binden spezielle werte an die Variablen/Paramter prozeduren hoeherer ordnung: - wir uebergeben der Prozedur Funktionen als Variablen/Parameter - beim aufruf der Prozedur uebergeben wir dann spezielle funktionen beachte: die funktionen filter1, etc konnten zwar implementiert und ausegfuehrt werden, unsere grammatik deckt dies aber noch nicht ab…

24 Funktionen von Funktionen verletzten die Sprachdefinitionen auf zwei Arten
(filter1 < (cons 4 empty) 5) Verletzung 1: (define (find aloir t) (cons? (filter1 eq-ir? aloir t))) Funktionsnamen als Argumente in Anwendungen Grund: Ein Argument ist ein Ausdruck und die Klasse der Ausdrücke enthält keine Funktionsnamen <exp> =   <var> | <con> | (<prm> <exp> ...<exp>) | (<fct> <exp> ...<exp>) | (cond (<exp> <exp>) ...(<exp> <exp>)) | (cond (<exp> <exp>) ...(else <exp>)) | ... The class of expressions does contain variables, but we agreed that they are only those variables mentioned in variable definitions and as function parameters. <var> <fct> = x | alon | ... area-of-disk | perimeter | ...

25 Funktionen von Funktionen verletzen grundlegende Sprachdefinitionen
Verletzung 2: Parameter werden an der ersten Stelle von Anwendungen benutzt (als wären sie Funktionen) (define (filter1 rel-op alon t) (cond [(empty? alon) empty] [else [(rel-op (first alon) t) (cons (first alon) (filter1 rel-op (rest alon) t))] [else (filter1 rel-op (rest alon) t)])])) The class of expressions does contain variables, but we agreed that they are only those variables mentioned in variable definitions and as function parameters. Grund: Unsere Grammatik erlaubt nur Namen von Funktionen und primitiven Operationen an dieser Stelle, jedoch keine Parameter

26 Scheme anpassen Die Namen von Funktionen und primitiven Operationen in die Definition von <exp> aufnehmen An der ersten Position in einer Anwendung sollten nicht nur Funktionsnamen und primitive Operationen erlaubt werden: Variablen, die Funktionsparametern entsprechen Allgemeiner noch: Irgendein Ausdruck <exp> =   <var> | <con> | <prm> | <fct> | (<exp> <exp> ...<exp>)

27 Scheme anpassen Die Anpassung der Grammatik, um Prozeduren höhere Ordnung zu unterstützen, verlängert die Grammatik nicht, sondern macht sie einfacher! <exp> =   <var> | <con> | (<prm> <exp> ...<exp>) | (<fct> <exp> ...<exp>) Mention the power of generalization… if we now wish to decide whether we can apply the substitution rule for functions, we must still ensure that all arguments are values, but we must recognize that function names and primitive operations count as values, too. an der stelle braeuchsten wir eigentlich keine unterscheidung zwischen var und fct anmore <var> = a | alon | perimeter | + | … <exp> =   <var> | <con> | <prm> | <fct> | (<exp> <exp> ...<exp>)

28 Scheme anpassen Auswertungsregeln ändern sich nicht
Was sich ändert, ist die Menge von Werten: sie enthält die Namen von Funktionen und primitiven Operationen <val> = <con> <val> =   <boo> | <sym> | <num> | empty | <lst> | <var> (names of defined functions) | <prm> | <fct> <lst> empty | (cons <val> <lst>) Mention the power of generalization… if we now wish to decide whether we can apply the substitution rule for functions, we must still ensure that all arguments are values, but we must recognize that function names and primitive operations count as values, too. hier machen wir jetzt keine unterscheidng mehr zwischen <var> und <fct> !! CHANGE: empty kann aus der Auflistung von <val> raus…

29 Intermediate Student Scheme Grammatik
<def> = (define (<fct> <var> ...<var>) <exp>) | (define <var> <exp>) | (define-struct <var> (<var> <var> ...<var>)) <exp> <var>| <bool> | <sym> | <prm> | <fct>| <num> | <lst> | (<exp> <exp> ...<exp>) | (cond (<exp> <exp>) ...(<exp> <exp>)) | (cond (<exp> <exp>) ...(else <exp>)) | (local (<def> ...<def>) <exp>) <var> <fct>   x | alon | ... area-of-disk | circumference | ... <boo>   true | false <sym>   'a | 'doll | 'sum | ... <num>   1 | -1 | 3/5 | 1.22 | ... <prm>   + | - | cons | first | rest | ... <lst> empty | (cons <val> <lst>) der hauptunterschied ist (<exp> <exp> … <exp>) zu vorher… vgl. hierzu folie 11 in T4 CHANGE: hier fehlen die dinge wie: and, or, … oder sind die teil von <prm>?

30 Verträge für abstrakte und polymorphe Funktionen
Wo liegt das Problem? Unsere neuen Funktionen akzeptieren einen Typ von Werten, den wir vorher nie als Daten verwendet haben: Primitive Operationen und andere Funktionen Um Verträge für Funktionen höherer Ordnung schreiben zu können, benötigen wir eine Methode, um die neue Klasse von Werten beschreiben zu können Daher haben wir das Schreiben von Verträgen für die Funktionen bisher aufgeschoben Dieses Thema werden wir auf den folgenden Folien behandeln When we first abstracted below and above into filter1, we did not formulate a contract. Unlike the functions we had defined before, filter1 consumed a type of values that we never before used as data: primitive operations and other functions. Still, we eventually agreed in plain English writing that filter1's first argument, rel-op, would always be a function that consumes two numbers and produces a boolean value.

31 Verträge für abstrakte und polymorphe Funktionen
Wir haben Verträge für Prozeduren erster Ordnung ;; rel-op : number number  ->  boolean Wir sagen “” beschreibt eine Klasse von Werten: die Klasse der Funktionen Namen links von  ””  legen fest, worauf jeder Element in der Klasse der Funktionen angewendet werden darf Der Name auf der rechten Seite von  ”” bestimmt, was jeder Wert der Klasse produzieren wird, wenn er auf die passenden Werte angewendet wird Allgemein: (A B  C) kennzeichnet die Klasse aller Funktionen, die ein Element aus A und eines aus B konsumieren und ein Element in C produzieren. Sie sind Funktionen „von A und B nach C“ The arrow notation is like the (listof ...) notation from the previous section. Both specify a class of data via a combination of other classes. For listof, we used data definitions to agree on what they mean. Others can follow the example and introduce their own abbreviations based on data definitions. For arrows, we just made an agreement, and it stays with us for good.

32 Beispiel: Vertragsdefinitionen
;; filter1 : (number number  ->  boolean) lon number  ->  lon ;; to construct the list of those numbers n on alon for which ;; (rel-op n t) evaluates to true (define (filter1 rel-op alon t) ...) Erste Version von filter1 Abstraktere Version von filter1 filter1 : (X number -> boolean) (listof X) number -> (listof X) The example contracts specify the class to which the first argument must belong not with a name introduced by a data definition but with a direct data definition, using the arrow notation … Version1: the first argument must be a function that consumes two numbers and produces a boolean value Version 2: the first argument must be a function that consumes a list of arbitrary data and a number and produces a boolean value X steht für eine beliebige Klasse von Scheme Daten. Wir können X mit irgendetwas ersetzen, so lange alle drei Vorkommen durch dasselbe ersetzt werden

33 Funktionen von Funktionen anwenden
listof number Ist diese Anwendung sinnvoll? (filter1 < (list ) 2 ) (number number -> boolean) Die zwei Klassen sind identisch zu den ersten beiden Argumentteilen des Vertrags von filter1, wenn X durch number ersetzt wird. filter1 : (X number -> boolean) (listof X) number -> (listof X) Before we do that, we should confirm that filter1 can indeed consume < and (list ), given its contract. A quick check shows that < makes sense because it belongs to the class (number number  ->  boolean) and (list ) makes sense because it belongs to (listof number) The two classes are identical to the first two argument parts of filter1's contract if X is replaced by number. Allgemein: Um sicher zu stellen, dass Argumente sinnvoll sind, müssen wir Ersetzungen für die Variablen in Verträgen finden, so dass der Vertrag und die Klassen der Argumente zusammen passen.

34 Eine neue Anwendung für filter1
filter1 benutzen, um alle Spielzeuge mit gleichem Namen aus einer Liste von Inventareinträgen (inventory records) zu extrahieren: ;; find : (listof IR) symbol -> (listof IR) (define (find aloir t) (filter1 eq-ir? aloir t)) ;; eq-ir? : IR symbol -> boolean (define (eq-ir? ir s) (symbol=? (ir-name ir) s)) Problem: „Schwellen“-Argument s ist ein symbol, nicht eine number  Konflikt mit dem momentanen Vertrag von filter1 Um das Problem zu lösen, führen wir eine neue Variable ein, TH für Schwellen (thresholds), welche für irgendeine Datenklasse steht: Now we can replace X with the name of one data collection and TH with that of a second one or possibly the same. In particular, the application (filter1 eq-ir? LOIR 'doll) works because a replacement of X by IR and TH by symbol in filter1's contract shows that the arguments are legitimate. CHANGE: Schwelle -> Schwellenwert oder Vergleichswert filter1 : (X TH -> boolean) (listof X) TH -> (listof X)

35 Zusammenfassung: Verträge und Typen
Funktionsverträge bestehen aus Typen: Basistyp, z.B. number, symbol, boolean, empty Definierter Typ, z.B. inventory-record, list-of-numbers, family-tree Funktionstyp, z.B. (number -> number) Parametrischer Typ, entweder ein definierter Typ oder ein Funktionstyp mit Typvariablen Um eine Funktion mit einem parametrischen Typ zu benutzen: Finde Ersetzung für Variablen im Funktionsvertrag mit Typen, welche den Klassen der aktuellen Argumente entsprechen Ist das nicht möglich, revidiere den Vertrag, oder stelle die Entscheidung in Frage, die Funktion wieder zu verwenden beachte: der Basistyp ist einfach - das ist was wir von Anfang an hatten definirter typ und funktionstyp muessen insofern geprueft werden, ob die verwendung dieser in der funktion auch den moeglichkeiten des dataentyps/der funktion entsprechen… bedenke: wir konnten filter1 allgemeiner verwenden als urspruenglich geplant ! ohne reimplementierung - sondern nur durch generalisierung des vertrages !

36 Kurzdarstellung Ähnlichkeiten in Definitionen Funktionen sind Werte
Abstraktionen aus Beispielen entwerfen Abstraktionen mit Funktionen als Werten entwerfen Funktionen “ad hoc” definieren “pipes-and-filters” Organisation von Berechnungen When we first learn to add, we use concrete examples. Later on, we study how to add two arbitrary numbers; that is, we form an abstraction of the addition operation. Much later still, we learn to formulate abstractions directly as expressions: expressions that compute the wage of some employee, expressions that convert temperatures, or expressions that determine the area of a geometric shape. In short, we initially go from concrete examples to abstraction, but eventually we learn to form abstractions directly without thinking (much) about concrete instances. In this section, we discuss a design recipe for creating abstractions from concrete examples. Later, we study additional approaches to function abstraction.

37 Rezept für das Abstrahieren von Beispielen
Wir haben bereits Designs abstrahiert, z.B. aufgrund zweier konkreter Funktionsdefinitionen: vergleiche sie, kennzeichne die Unterschiede, abstrahiere. Jetzt formulieren wir diese Schritte als Rezept: Vergleiche die Beispiele und rahme Unterschiede ein Abstrahiere, falls die Rahmen Werte enthalten Teste die Gültigkeit der Abstraktion Formuliere den Vertrag der Abstraktion

38 Beispiele vergleichen
Wenn Sie zwei Funktionsdefinitionen finden, die beinahe gleich sind: Vergleiche sie und markieren die Unterschiede in Kästchen Wenn Kästchen Werte enthalten, können wir abstrahieren ;; convertCF : lon -> lon (define (convertCF alon) (cond [(empty? alon) empty] [else (cons (C->F (first alon)) (convertCF (rest alon))] )) ;; names : loIR -> los (define (names aloIR) (cond [(empty? aloIR) empty] [else (cons (IR-name (first aloIR)) (names (rest aloIR)))] )) convertCF: rechne liste von (numbers) C (z.B. euro) in liste von F um (z.B. US-Dollar, auch list of numbers) names: hole aus liste von IR (inventarliste) die ganzen namen heraus (list of symbols) Jedes Kästchen enthält einen Funktionswert, also können wir abstrahieren ...

39 Zwei Schritte der Abstraktion
Ersetze die Inhalte korrespondierender Kästchenpaare mit neuen Namen und füge diese Namen zur Parameterliste hinzu. Es werden soviele Namen benötigt, wie es Kästchenpaare gibt (define (convertCF f alon) (cond [(empty? alon) empty] [else (cons (f (first alon)) (convertCF (rest alon)))] )) (define (names f aloIR) (cond [(empty? aloIR) empty] [else (cons (f (first aloIR)) (names (rest aloIR)))] ))

40 Zwei Schritte der Abstraktion
Die zwei Definitionen müssen jetzt dieselben sein, bis auf die Funktionsnamen Um die Abstraktion zu erhalten, ersetze die Funktionsnamen systematisch mit einem neuen Namen (define (map f lon) (cond [(empty? lon) empty] [else (cons (f (first lon)) (map f (rest lon)))])) We use the name map for the result in our running example, because it is the traditional name in programming languages for this specific function. map stellt ein übliches Pattern für Operationen auf Listen dar  es abstrahiert über die Operation, die auf die Listenelemente angewandt werden soll

41 Die Abstraktion testen
Ziel: Sicherstellen, dass die neue Funktion eine korrekte Abstraktion der Originalfunktionen ist. Vorgehen: Definiere Originalfunktionen mittels Applikation der abstrakten Funktion Teste die neuen Versionen mit den Beispielen, die beim Entwurf der Originalfunktionen formuliert wurden

42 Die Originalfunktionen als Spezialisierung der Abstraktion definieren
Annahmen: Abstrakte Funktion heißt f-abstract Eine Originalfunktion heißt f-original und erwartet ein Argument Wenn sich f-original von der anderen konkreten Funktion in der Verwendung eines Wertes unterscheidet, z.B. boxed-value, dann definieren wir die folgende Funktion: Für jeden zulässigen Wert V sollte folgendes gelten: (define (f-from-abstract x) (f-abstract boxed-value x)) wenn wir f-orginal aufrufen (lezte Zeile), dann muss das gleiche rauskommen wie mit der neuen abstrakten funktion (angepasst an f-orginal) - hier heisst die f-from-abstract boxed-value ist ein wert: konstante, funktion, zahl, … (f-from-abstract V) = (f-original V)

43 Die Originalfunktionen als Spezialisierung der Abstraktion definieren
;; convertCF-from-map : lon -> lon (define (convertCF-from-map alon) (map C->F alon)) ;; names-from-map : loIR -> los (define (names-from-map aloIR) (map IR-name aloIR)) Um sicher zu stellen, dass diese beiden Definitionen zu den alten äquivalent sind, d.h. dass map eine korrekte Abstraktion ist, wende diese beiden Funktionen auf Beispiele an, die für die Entwicklung von convertCF und names spezifiziert wurden

44 Den Vertrag formulieren
Ziel: Einen Vertrag für die abstrakte Funktion formulieren Hinweis: Normalerweise kann man den Vertrag für die abstrakte Funktion nicht formulieren, indem man sie als Abstraktion der einen oder anderen Originalfunktion betrachtet Betrachten wir map als Abstraktion von convertCF, so erhalten wir: (number -> number) (listof number) -> (listof number) Betrachten wir map als Abstraktion von names, so erhalten wir: (IR -> symbol) (listof IR) -> (listof symbol) Der erste Vertrag wäre nutzlos im zweiten Fall und umgekehrt

45 Allgemeine Verträge formulieren
Durch Betrachten der Definition sehen wir: map wendet sein erstes Argument – eine Funktion f – auf jedes Element des zweiten Arguments – eine Liste l – an Impliziert: f muss die Daten verarbeiten, die l enthält Daher hat f den Vertrag X -> ???, wenn l Werte der Klasse X enthält Wenn f Werte in Y produziert, dann produziert map eine Liste von Ys: map : (X -> Y) (listof X) -> (listof Y) wir haben eine eingabe-liste und eine ausgabe-liste und die funktion f muss jeweils ein element der eingabe-liste auf ein element der ausgabe-liste mappen we must understand what map does and then fix a contract. The contract we formulated says that map can produce a list of Ys from a list of Xs and a function from X to Y -- no matter for what collection of X and Y stand.

46 Neue Einsatzmöglichkeiten abstrakter Funktionen
Eine abstrakte Funktion ist allgemein in einem breiteren Kontext nützlich, als zunächst angenommen: So kann map jedesmal benutzt werden, wenn wir eine neue Liste durch Bearbeitung einer bestehenden Liste erzeugen möchten ;; list-of-squares : (listof numbers) -> (listof numbers) ;; constructs the list of squares of the elements of alon (define (list-of-squares alon) (cond [(empty? alon) empty] [else (cons (sqr (first alon)) (list-of-squares (rest alon)))])) (list-of-squares (list )) --> (list ) Once you have abstracted two (or more) functions, you should check whether there are other uses for the abstract function. (define (list-of-squares list) (map sqr list))

47 Verträge und neue Einsatzmöglichkeiten abstrakter Funktionen
Frage: Wie entdecken wir neue Einsatzmöglichkeiten abstrakter Funktionen? Dafür gibt es kein Rezept Es ist Übungssache, einen Blick dafür zu entwickeln, ob abstrakte Funktionen zu Situationen passen Die Formulierung des Vertrags ist wichtig, um die Nützlichkeit einer abstrakten Funktion zu erhöhen: Formuliere Verträge, welche die Anwendbarkeit abstrakter Funktionen im allgemeinst möglichen Kontext beschreiben Once you have abstracted two (or more) functions, you should check whether there are other uses for the abstract function.

48 Allgemeine Verträge formulieren
Das Abstrahieren von Verträgen folgt dem gleichen Rezept wie das Abstrahieren von Funktionen: Vergleiche Verträge von Beispielen, die wir abstrahieren wollen Ersetze spezifische Klassen an korrespondierenden Positionen, eine nach der anderen, um den Vertrag allgemeiner zu machen Überprüfe, ob der Vertrag die spezifischen Instanzen der abstrahierten Funktion richtig beschreibt  (number -> number) (listof number) -> (listof number) X Y It is straightforward to check that by replacing X with number, respectively with IR, and Y with number, respectively with symbol, we get the first, respectively the second, of the intermediate contracts. (IR -> symbol) (listof IR) -> (listof symbol) (X -> Y) (listof X) -> (listof Y)

49 [Wir erinnern uns…] Prozeduren erster Ordnung: Machen Berechnungen unabhängig von den Werten bestimmter Daten (Zahlen/Symbole) usw. Prozeduren höherer Ordnung: Drücken allgemeine Berechnungsmethoden unabhängig von den jeweilig beteiligten Funktionen aus Schauen wir uns an, was das bedeutet - am Beispiel von map …

50 map als Abstraktionsbarriere
(define (list-of-squares alon) (cond [(empty? alon) empty] [else (cons (sqr (first alon)) (list-of-squares (rest alon)))])) Diese obige Version von list-of-squares ist aus zwei Gründen nicht optimal: Sie hebt die Element-für-Element Verarbeitung der Listen hervor Verrät zu viele Details über die Implementierung der Listen, darüber wie Listenelemente extrahiert und kombiniert werden Die Operation, welche auf jedem Listenelement angewandt wird, ist fest verdrahtet CHANGE: Element-fuer-Element -> elementweise

51 map als Abstraktionsbarriere
(define (list-of-squares list) (map sqr list)) Die map-Version von list-of-squares unterdrückt dieses Detaillevel Sie betont, dass Quadrieren eine Transformation einer Liste von Elementen in eine andere Liste ist Höhere Abstraktion beim Umgang mit Listen Computer führt den gleichen Prozess aus Wir denken anders über den Prozess! zusaetzliche Inderektionsstufe erhoeht die Abstraktion der Funktion

52 Funktionen höherer Ordnung als Abstraktionsbarrieren: Beispiel von map
Eine Abstraktionsbarriere isoliert die Implementierung von Prozeduren, die Listen transformieren, von den Details, wie die Elemente der Liste extrahiert und kombiniert werden Operation, die Sequenzen in Sequenzen umwandelt MAP Unwichtige Details, wie Sequenzen implementiert sind CHANGE: MAP -> map Man kann die Implementierung der Listen unabhängig von der Umwandlungsfunktion variieren

53 Benutzung von map für Bäume
map zusammen mit Rekursion ist eine mächtige Abstraktion, um baumartige Datenstrukturen zu bearbeiten ;; scale-tree : (treeof numbers) number -> (treeof numbers) ;; constructs a new list by multiplying ;; each element of tree by num (define (scale-tree tree num) (map (local ((define (scale-st sub-tree) (cond [(cons? sub-tree) (scale-tree sub-tree num)] [else (* sub-tree num)]))) scale-st) tree) ) meta-aufruf: (map scale-st tree) map ruft die funktion (scale-st) fuer das erste element der liste auf: sub-tree bei beliebig geschachtelten listen - muss scale-st auf 2 faellen funktionieren - element - oder wieder eine liste

54 Abstrahieren von Schablonen
Beim Design von Funktionen orientieren wir uns an die Schablone der Datendefinition Definitionen sehen ähnlich aus; wir abstrahieren sie Wir könnten direkt von den Schablonen abstrahieren Eine list-of-numbers ist: entweder empty oder (cons n l) wo n is a number l is a list-of-numbers (define (fun-for-l l) (cond [(empty? l) ...] [else ... (first l) ... (fun-for-l (rest l)) ...])) While this topic is highly advanced and still a subject of research in the area of programming languages Um eine konkrete Funktion f abzuleiten, füllen wir zwei Lücken: Im ersten Fall setzen wir typischerweise einen konkreten Wert ein Im zweiten Fall kombinieren wir (first l) mit (f (rest l))

55 Abstrahieren von Schablonen
fold stellt ein übliches Pattern für Operationen auf Listen dar Anwendung einer binären Operation auf dem ersten Element und auf dem Ergebnis der Anwendung von fold auf dem Rest der Liste ;; fold : Y (X Y -> Y) (listof X) -> Y (define (fold init combine-op lst) (cond [(empty? lst) init] [else (combine-op (first lst) (fold init combine-op (rest lst)))])) sum: berechnet die summe aller elemente einer liste product: berechnt das produkt aller elemente einer liste ;; sum : (listof number) -> number (define (sum lst) (fold 0 + lst)) ;; product : (listof number) -> number (define (product lst) (fold 1 * lst))

56 [Notiz: Zusammenfalten in Scheme]
“Zusammenfalten” ist eine sehr häufige Operation über Listen, so dass es dafür zwei vordefinierte Funktion in Scheme gibt: foldr faltet zusammen von rechts nach links foldl faltet zusammen von links nach rechts ;; foldr : (X Y -> Y) Y (listof X) -> Y ;; (foldr f base (list x x-n)) = ;; (f x (f x-n base)) (define (foldr f base alox) ...) ;; foldl : (X Y -> Y) Y (listof X) -> Y ;; (foldl f base (list x x-n)) = ;; (f x-n ... (f x-1 base)) (define (foldl f base alox) ...)

57 sort von fold ableiten Können wir fold verwenden, um Listen zu sortieren? Erinnern wir uns an insertion sort…

58 [Erinnerung insert] insert für den Fall, dass alon nicht leer ist:
;; insert : number list-of-numbers -> list-of-numbers (define (insert n alon) (cond [(empty? alon) (cons n empty)] [(<= n (first alon)) (cons n alon)] [else (cons (first alon) (insert n (rest alon)))])) insert für den Fall, dass alon nicht leer ist: Überspringe alle Elemente in alon bis zu dem ersten Element von alon, ej+1, das größer ist als n. Füge nzwischen elj und elj+1 ein. n Other list-processing functions can be defined in a similar manner. n elj+1 elj eln alon eli,i = [1… j], eli <= n update: j-Indizes in der Animation leider falsch

59 [Erinnerung: insertion sort]
;; sort : list-of-numbers  ->  list-of-numbers ;; creates a sorted list of numb. from numbers in alon (define (sort alon) (cond [(empty? alon) empty] [else (insert (first alon) (sort (rest alon)))])) unsorted sorted n n

60 sort von fold ableiten init  empty combine-op  insert
;; fold : Y (X Y -> Y) (listof X) -> Y (define (fold init combine-op lst) (cond [(empty? lst) init] [else (combine-op (first lst) (fold init combine-op (rest lst)))])) init  empty combine-op  insert ;; sort : (listof number) -> (listof number) (define (sort lst) (local ((define (insert an alon) ... ) (fold empty insert lst))) Other list-processing functions can be defined in a similar manner.

61 sort @ work 2 3 2 8 (sort lst) = (fold empty insert (list 3 2 8))
= (insert 3 (fold empty insert (list (2 8)))) = (insert 3 (insert 2 (fold empty insert (list 8)))) = (insert 3 (insert 2 (insert 8 (fold empty insert ())))) = (insert 3 (insert 2 (insert 8 ()))) = (insert 3 (insert 2 (list 8))) = (insert 3 (list 2 8) = (2 3 8)

62 Abstraktion und „eindeutiger Kontrollpunkt”?
Eine Abstraktion vereinfacht oft andere Definitionen Der Prozess der Abstraktion kann Probleme mit existierenden Funktionen aufdecken Abstrahierte Funktionsdefinitionen sind flexibler und breiter einsetzbar als spezialisierte Definitionen Der Hauptvorteil von Abstraktion liegt jedoch darin, dass Sie einen eindeutigen Kontrollpunkt für die Funktionalität in einem Programm darstellt Die Definitionen für eine bestimmte Aufgabe werden (so weit wie möglich) an einem Ort vereint Separation of Concerns wenn sie eine Funktion wiederverwenden, die schon haeufig (in verschiedenen Kontexten) getestet wurde und sich bewaehrt hat, koennen sie davon ausgegen, dass diese funktion eigentlich keinen fehler hat

63 Warum „Eindeutiger Kontrollpunkt”?
Wenn sich alle Definitionen für eine bestimmte Aufgabe sich an einer Stelle befinden, wird das Programm leichter zu warten Unter Wartung eines Programms verstehen wir: Behebung von Fehlern, so dass es in vorher nicht getesteten Fällen funktioniert Erweiterung des Programms, um mit neuen oder nicht vorgesehenen Situationen zurechtzukommen Änderung der Darstellung von Informationen als Daten (z.B. Kalenderdaten) Einige Schätzungen gehen davon aus, dass die Wartung eines erfolgreichen Programms den Hauptteil seiner Lebensdauer ausmacht (60-80%)

64 Warum „eindeutiger Kontrollpunkt”?
Durch Änderung einer einzigen Definition können wir an vielen Stellen Fehler beheben und Verbesserungen erzielen Ein ebenso wichtiger Vorteil von abstrahierten Definitionen Beispiel: Zwei Änderungen an filter1 auf den nächsten zwei Folien: Die erste Änderung glättet den geschachtelten cond-Ausdruck Die zweite Änderung verwendet einen local-Ausdruck, um den geschachtelten cond-Ausdruck lesbarer zu machen etwas, was ein erfahrener Programmierer in Betracht ziehen könnte

65 Warum „eindeutiger Kontrollpunkt”?
(define (filter1 rel-op alon t) (cond [(empty? alon) empty] [else [(rel-op (first alon) t) (cons (first alon) (filter1 rel-op (rest alon) t))] [else (filter1 rel-op (rest alon) t)])])) (define (filter1 rel-op alon t) (cond [(empty? alon) empty] [(rel-op (first alon) t) (cons (first alon) (filter1 rel-op (rest alon) t))] [else (filter1 rel-op (rest alon) t)])) The first variant flattens the nested cond-expression, something that an experienced programmer may wish to do. The second variant uses a local-expression that makes the nested cond-expression more readable.

66 Warum „eindeutiger Kontrollpunkt”?
(define (filter1 rel-op alon t) (cond [(empty? alon) empty] [else [(rel-op (first alon) t) (cons (first alon) (filter1 rel-op (rest alon) t))] [else (filter1 rel-op (rest alon) t)])])) (define (filter1 rel-op alon t) (cond [(empty? alon) empty] [else (local ((define first-item (first alon)) (define rest-filtered (filter1 rel-op (rest alon) t))) [(rel-op first-item t) (cons first-item rest-filtered)] [else rest-filtered]))])) The first variant flattens the nested cond-expression, something that an experienced programmer may wish to do. The second variant uses a local-expression that makes the nested cond-expression more readable.

67 Warum „Eindeutiger Kontrollpunkt”?
Alle Verwendungen von filter1, inklusive derer, die zur Definition von less-than1 and greater-than1 benutzt werden, profitieren von den Änderungen Es würden alle Verwendungen der Funktion davon profitieren, wenn die Änderung einen logischen Fehler beheben würde Letztendlich ist sogar möglich, einer abstrahierten Funktion neue Aufgaben zuzuweisen, z.B. einen Mechanismus, der zählt, wie viele Elemente gefiltert werden Alle Verwendungen der Funktion würden von der neuen Funktionalität profitieren

68 Warum „Eindeutiger Kontrollpunkt”?
Wenn alles an einer Stelle steht: Verstehen, was zu reparieren, erweitern oder zu ändern ist, bedeutet, nur eine einzige Funktion zu verstehen Einen Fehler zu beheben, bedeutet, ihn in einer Funktion zu beheben, nicht in mehreren ähnlichen Versionen Die Fähigkeiten einer Funktion zu erweitern, bedeutet, nur eine Funktion zu ändern, und nicht ähnliche Kopien Eine Datenrepräsentation zu ändern, bedeutet, eine allgemeine Daten-Traversierungs-Funktion zu ändern, und nicht all jene, die von der gleichen Schablone stammen Richtlinie für die Erstellung von Abstraktionen: Erstelle eine Abstraktion, anstatt Teile des Programms zu kopieren und zu verändern

69 Die Rolle von Abstraktion
Die Erfahrung lehrt uns, dass die Wartung von Software teuer ist Programmierer können die Wartungskosten senken, indem sie Programme richtig organisieren Erstes Prinzip: Passe die Struktur der Funktion an die Struktur der Daten an. Erleichtert Änderungen und Erweiterungen von Funktionen, wenn die Menge der möglichen Eingabedaten sich verändert Zweites Prinzip: Führe angemessene Abstraktionen ein Jede abstrahierte Funktion erzeugt einen eindeutigen Kontrollpunkt für mindestens zwei verschiedene Funktionen, oft auch für mehr prinzipien um wartung von software einfacher zu machen

70 Die Rolle von Abstraktion
Die besten Programmierer verändern ihre Programme, um neue Abstraktionen zu erzeugen Unser Designrezept zur Abstraktion von Funktionen ist das grundlegende Werkzeug dazu Die Anwendung erfordert Übung. Während wir üben, erweitern wir unsere Fähigkeit, Abstraktionen zu erzeugen und zu nutzen Wir benutzen funktionale Abstraktion zum Erlernen dieser Konzepte Nicht alle Sprachen geben uns die Freiheit, Funktionen so leicht zu abstrahieren wie in Scheme, jedoch verfügen moderne Sprachen oft über ähnliche Konzepte Mit mächtigen Sprachen wie Scheme zu üben ist die beste Vorbereitung

71 Überblick Ähnlichkeiten in Definitionen Funktionen sind Werte
Abstraktionen aus Beispielen entwerfen Abstraktionen mit Funktionen als Werten entwerfen Funktionen “ad hoc” definieren “pipes-and-filters” Organisation von Berechnungen

72 Funktionen, die Funktionen erzeugen
Ausdrücke können im „neuen“ Scheme zu Funktionen ausgewertet werden Da der Rumpf einer Funktion auch ein Ausdruck ist, kann eine Funktion eine Funktion erzeugen Funktionen, die Funktionen erzeugen, sind besonders dann nützlich, wenn sich die erzeugte Funktion an die Argumente der erzeugenden Funktion zur Zeit der Anwendung „erinnert“ (define (add-10 x) (+ x 10)) (define (add-5 x) (+ x 5)) (define (h x) (cond [(< x 5) add-5] [else add-10])) (define addX (h 12)) (addX 7) -> 17 (define (f x) first) (define (g x) f) (define (h x) (cond [(empty? x) f] [(cons? x) g])) beachte: die definitionen von f und g sind eher nutzlos… aber zeigen das prinzip wir kommen gleich zu sinnvolleren beispielen The body of f is first, a primitive operation, so applying f to any argument always evaluates to first. Similarly, the body of g is f, so applying g to any argument always evaluates to f. Finally, depending on what kind of list we supply as an argument to h, it produces f or g. None of these examples is useful but each illustrates the basic idea. In the first two cases, the body of the function definition is a function. In the last case, it evaluates to a function. The examples are useless because the results do not contain or refer to the argument. beim aufruf von h bekommen wir eine funktion (f oder g) zurueck !

73 Funktionen mit „Gedächtnis“
;; add : number  ->  (number  ->  number) ;; to create a function that adds x to its input (define (add x) (local ((define (x-adder y) (+ x y))) x-adder)) Interessante Eigenschaft von add: Das Ergebnis „erinnert sich“ an den Wert des Arguments x zum Applikationszeitpunkt Jedes Mal, wenn wir das Ergebnis verwenden, benutzt es den Wert von x zur Zeit der Applikation The function add consumes a number; after all, x is added to y. It then defines the function x-adder with a local-expression. The body of the local-expression is x-adder, which means the result of add is x-adder. Funktionen mit „Gedächtnis“ entstehen durch die Kombination von local-Ausdrücken und Funktionen höherer Ordnung

74 Funktionen mit „Gedächtnis“
add 4 2 9 7 5 (add 5) 8 5 x 8 2 1 10 9 (add 8) (define f (add 5)) = (define f (local ((define (x-adder y) (+ 5 y))) x-adder)) = (define f (local ((define (x-adder5 y) (+ 5 y))) x-adder5)) = (define (x-adder5 y) (+ 5 y)) (define f x-adder5) Remember that as we lift a local definition to the top-level definitions, we also rename the function in case the same local is evaluated again. umbenennung in x-adder5 da wir die local definition auf da top-level ziehen wollen letzte zeile zeig die korrespondierenden definition auf dem top-level (f 10) = (x-adder5 10) = (+ 5 10) = 15

75 Funktionen mit Gedächtnis als Abstraktionsmittel
Funktionen mit Gedächtnis können das Rezept zur Erstellung abstrakter Funktionen vereinfachen Bisheriges Rezept: Vergleiche und rahme Unterschiede ein Ersetze den Inhalt von Kästchen mit Variablen Binde diese Variablen, indem sie zur Argumentliste hinzugefügt werden Alternatives Rezept: Schließe die Definition in ein local ein, wobei der Inhalt der Kästchen mit freien Variablen ersetzt wird Stelle dem local Block eine Funktion voran, welche die entstandenen freien Variablen bindet Adding the introduced variables to the list of arguments is only one alternative…

76 Abstrahieren mit Funktionen mit Gedächtnis
;; less-than: lon number  ->  lon ;; to construct a list of those numbers ;; on alon that are less than t (define (less-than alon t) (cond [(empty? alon) empty] [else [(< (first alon) t) (cons (first alon) (less-than(rest alon) t))] [else (less-than(rest alon) t)])]))

77 Abstrahieren mit Funktionen mit Gedächtnis
;; greater-than: lon number  ->  lon ;; to construct a list of those numbers ;; on alon that are greater than t (define (greater-than alon t) (cond [(empty? alon) empty] [else [(> (first alon) t) (cons (first alon) (greater-than (rest alon) t))] [else (greater-than (rest alon) t)])])) The difference between the two functions is the comparison operator. The left uses <, the right one >. Following the first example, we abstract over the two functions with an additional parameter that stands for the concrete relational operator in below and above:

78 Abstrahieren mit Funktionen mit Gedächtnis
(define (filter2 rel-op) (local ( (define (abs-fun alon t) (cond [(empty? alon) empty] [else [(rel-op (first alon) t) (cons (first alon) (abs-fun (rest alon) t))] [else (abs-fun (rest alon) t)])]))) abs-fun) ) add: hat sich eine Zal gemerkt filter2: merkt sich einen Operator Genau wie add erhält filter2 ein Argument, definiert eine Funktion und gibt sie als Ergebnis zurück. Das Ergebnis erinnert sich an rel-op, für immer.

79 Alternatives Abstraktionsrezept
Die Abstraktion: Stelle eine der Funktionen in eine local-expression und benutze den Namen der Funktion als Body von local: Erzeuge die abstrakte Funktion durch Auflistung der Namen in Kästchen als Parameter: Falls op1 oder op2 ein spezielles Symbol ist, z.B. <, benenne es mit etwas, das im neuen Kontext aussagekräftiger ist (local ((define (concrete-fun x y z) ... op1 ... op2 ...)) concrete-fun) (define (abs-fun op1 op2) (local ((define (concrete-fun x y z) ... op1 ... op2 ...)) concrete-fun))

80 Alternatives Abstraktionsrezept
Der Test: Leite die konkreten Funktionen wie zuvor als Instanzen von filter2 ab. Beispiel: less-than und greater-than als Instanz von filter2: Der Vertrag: Der Vertrag einer mit dem neuen Rezept erzeugten abstrakten Funktion enthält zwei Pfeile. Die Funktion erzeugt eine Funktion Um dies darzustellen, muss der Typ auf der rechten Seite des ersten Pfeils einen weiteren Pfeil enthalten. Beispiel: Der Vertrag für filter2: (define less-than2 (filter2 <)) (define greater-than2 (filter2 >)) We simply apply filter2 to the contents of the box in the respective concrete function and that application produces the old function. The contract for filter2: consumes a comparison function and produces a concrete filter-style function. ;; filter2 : (X Y -> boolean) ->  ((listof X) Y -> (listof X))

81 Überblick Ähnlichkeiten in Definitionen Funktionen sind Werte
Abstraktionen aus Beispielen entwerfen Abstraktionen mit Funktionen als Werten entwerfen Funktionen “ad hoc” definieren “pipes-and-filters” Organisation von Berechnungen

82 Motivation für „ad hoc“ Funktionsdefinitionen
Die Applikation abstrakter Funktionen erfordert oft die Definition von Hilfsfunktionen filter1 ist mit <ir, eq-ir, … <ihre Funktion> benutzbar ;; find : (listof IR) symbol -> (listof IR) (define (find aloir t) (filter1 eq-ir? aloir t)) ;; eq-ir? : IR symbol -> boolean (define (eq-ir? ir s) (symbol=? (ir-name ir) s)) (define (filter1 rel-op alon t) (cond [(empty? alon) empty] [else [(rel-op (first alon) t) (cons (first alon) (filter1 rel-op (rest alon) t))] (filter1 rel-op (rest alon) t)])])) Zur Erinnerung: die Definition von filter1 We use local to indicate that the application of f and the auxiliary function definition belong together (guidelines in 18) The second alternative is feasible because the names of functions -- like eq-ir? -- are now legitimate expressions and can play the role of local's body. Thus the local-expression introduces a function definition and returns the function as its result.

83 Motivation für „ad hoc“ Funktionsdefinitionen
Falls Hilfsfunktionen nur als Argumente einer abstrakten Funktion f benutzt werden, verwenden wir local ;; find : list-of-IRs symbol -> boolean (define (find aloir t) (local ((define (eq-ir? ir s) (symbol=? (ir-name ir) s))) (filter1 eq-ir? aloir t))) ;; find : list-of-IRs number -> boolean (define (find aloir t) (filter1 (local ((define (<ir ir p)(< (ir-price ir) p))) <ir) aloir t ) We use local to indicate that the application of f and the auxiliary function definition belong together (guidelines in 18) The second alternative is feasible because the names of functions -- like eq-ir? -- are now legitimate expressions and can play the role of local's body. Thus the local-expression introduces a function definition and returns the function as its result. beachte: hier sind 2 Schreibweisen mit dem gleichen Resultat: local am Anfang (1. version) und local mittendrin (2. version) 1. version: (<exp1>) -> local-expression 2. version: (<exp1> <exp2> <exp3> <exp4>) -> <exp2 ist local expression

84 Motivation für „ad hoc“ Funktionsdefinitionen
Da gute Programmierer abstrakte Funktionen benutzen und ihre Programme ordentlich organisieren, gibt es in Scheme eine Abkürzung für diese spezielle, häufige Verwendung von local Diese Abkürzung wird lambda-Ausdruck genannt Das erleichtert die „spontane“ Einführung von Funktionen wie eq-ir? oder <ir Nächste Folien: Syntax und Semantik von lambda-Ausdrücken Pragmatik

85 Syntax von lambda Ein lambda-Ausdruck ist ein neuer Typ von Ausdruck, gekennzeichnet durch das Schlüsselwort lambda Ein lambda-Ausdruck definiert eine anonyme Funktion Die Sequenz von Variablen hinter dem Schlüsselwort lambda sind die Parameter der Funktion Die dritte Komponente ist der Body der Funktion Beispiele <exp> = (lambda (<var> ... <var>) <exp>) (define (plus4 x) (+ x 4)) ist gleichbedeutend zu (define plus4 (lambda (x) (+ x 4))) Im Allgemeinen wird lambda genau wie define verwendet, um Prozeduren zu definieren. Allerdings wird bei lambda kein Name spezifiziert. (lambda (x c) (> (* x x) c)) (lambda (ir p) (< (ir-price ir) p)) (lambda (ir s) (symbol=? (ir-name ir) s))

86 Scope und Semantik von lambda
a-new-name darf nicht in exp vorkommen (lambda (x x-n) exp) (local ((define (a-new-name x x-n) exp)) a-new-name) (lambda (x x-n) exp) führt x x-n als gebundene Variablen ein, deren Scope exp ist. if exp contains further binding constructs (say, a nested local-expression), then the scope of the variables may have a hole.

87 lambda statt local ;; find : list-of-IRs number -> boolean
(define (find aloir t) (filter1 (local ((define (<ir ir p) (< (ir-price ir) p))) <ir) aloir t) ) ;; find : list-of-IRs number -> boolean (define (find aloir t) (filter1 (lambda (ir p) (< (ir-price ir) p)) aloir t) )

88 lambda statt local Drei Schritte: Erzeuge einen Prozedurwert
;; add : number -> (number -> number) ;; to create a function that adds x to its input (define (add x) (local ((define (x-adder y) (+ x y))) x-adder)) Drei Schritte: Erzeuge einen Prozedurwert Assoziiere einen Namen mit dem Wert Werte den Namen aus, um den Wert zurückzuliefern Wozu der Zwischenschritt? Besser: direkt den erzeugten Prozedurwert zurückliefern ;; add : number -> (number -> number) ;; to create a function that adds x to its input (define (add x) (lambda (y) (+ x y)))

89 lambda statt local Quiz: Kann ich hier local mit lambda ersetzen?
(define (filter2 rel-op) (local ( (define (abs-fun alon t) (cond [(empty? alon) empty] [else [(rel-op (first alon) t) (cons (first alon) (abs-fun (rest alon) t))] [else (abs-fun (rest alon) t)])])) ) abs-fun)

90 Semantik von lambda (lambda (x x-n) exp) (local ((define (a-new-name x x-n) exp)) a-new-name) Grundlegendes zur Auswertung von lambda-Expressions: Ein lambda-Ausdruck ist ein Wert, weil Funktionen Werte sind Die Anwendung von lambda-Ausdrücken erfolgt nach den üblichen Regeln der Funktionsanwendung, vorausgesetzt, wir werten die Kurzform zuerst aus if exp contains further binding constructs (say, a nested local-expression), then the scope of the variables may have a hole.

91 Semantik von lambda Zur Erläuterung evaluieren wir die folgende Applikation: (filter1 (lambda (ir p) (< (ir-price ir) p)) (list (make-ir 'doll 10)) 8) (define (filter1 rel-op alon t) (cond [(empty? alon) empty] [else [(rel-op (first alon) t) (cons (first alon) (filter1 rel-op (rest alon) t))] (filter1 rel-op (rest alon) t)])])) Zur Erinnerung: die Definition von filter1

92 Scope und Semantik von lambda
(filter1 (lambda (ir p) (< (ir-price ir) p)) (list (make-ir 'doll 10)) 8) ... = (filter1 (local ((define (<ir ir p) (< (ir-price ir) p))) <ir) = (filter1 <ir (list (make-ir 'doll 10)) 8) substituiere lambda mit local For the last step, the local definition of <ir is lifted and added to the top-level definitions. From here, the evaluation proceeds as known schritte - lamda wird mit local ersetzt - local wird auf top-level gehoben - rest wie gehabt

93 Semantik von lambda Wir können die Semantik von lambda direkter erklären: ( (lambda (x x-n) exp) val val-n ) = exp mit x x-n ersetzt durch val val-n For the last step, the local definition of <ir is lifted and added to the top-level definitions. From here, the evaluation proceeds as known

94 Semantik von lambda Zur Erläuterung evaluieren wir erneut die Applikation: (filter1 (lambda (ir p) (< (ir-price ir) p)) (list (make-ir 'doll 10)) 8) (define (filter1 rel-op alon t) (cond [(empty? alon) empty] [else [(rel-op (first alon) t) (cons (first alon) (filter1 rel-op (rest alon) t))] (filter1 rel-op (rest alon) t)])])) Zur Erinnerung: die Definition von filter1

95 Scope und Semantik von lambda
(filter1 (lambda (ir p) (< (ir-price ir) p)) (list (make-ir 'doll 10)) 8) = (cond [((lambda (ir p) (< (ir-price ir) p)) (make-ir 'doll 10) 8) (cons (first (list (make-ir 'doll 10))) (rest (list (make-ir 'doll 10))) 8))] [else (filter1 (lambda (ir p) (< (ir-price ir) p)) ]) [(< (ir-price (make-ir 'doll 10)) 8) 8)]) = ... 1. El. der Liste beachte liste alon nicht leer -> else… d.h. das ist der 2. cond-case

96 Pragmatik von lambda Richtlinie für Lambda Ausdrücke
Benutze lambda-Ausdrücke, wenn eine Funktion nicht rekursiv ist und nur einmal, als Argument, gebraucht wird If we were to apply the guideline to the programs in the preceding sections, we would quickly see how much lambda simplifies the use of abstracted functions. For that reason, Scheme offers many abstracted functions in its libraries. In future sections, we will encounter many more examples where lambda-expressions are convenient.

97 Warum “Lambda” ? Das Wort „Lambda“ stammt aus -Kalkül - ein mathematisches Kalkül Es wurde eingeführt von Alonso Church Im Wesentlichen der Kern von Scheme (eine minimale aber komplette Sprache): Nur Lambda-Abstraktion und Anwendung Keine primitiven Typen/Prozeduren Keine Spezialformen etc. Grundlegendes Werkzeug für mathematische Untersuchungen und formale Behandlung von Programmiersprachen Zunächst kann man Lambda als erzeuge-Prozedur betrachten Aber schauen wir uns kurz an, was man mit Lambda alleine alles machen kann!

98 Das ultimative Lambda …
Berechnen ohne primitive Werte/Typen, spezielle Formen? Ja! Alles wird durch Funktionen modelliert … z.B. die primitiven Operatoren and/or (define my-and (lambda (op1 op2) (cond [op1 (cond [op2 true] [else false])] [else false]))) (define my-or (lambda (op1 op2) (cond [op1 true] [else (cond [op2 true] [else false])])))

99 Das ultimative Lambda …
OK, aber wozu soll das gut sein? Um die Komponierbarkeit zu erhöhen  Closure-Prinzip! Spezielle Formen können nicht als Parameter einer Prozedur übergeben werden … Zur Illustration schauen wir uns die Scheme Funktionen andmap und ormap an

100 Das ultimative Lambda …
Der Fall andmap und ormap ;; andmap : (X -> boolean) (listof X) -> boolean ;; determines whether p holds for every item on alox ;; (andmap p (list x x-n)) = ;; (and (p x-1) (and ... (p x-n))) (define (andmap p alox) (cond [(empty? alox) true] [else (and (p (first alox)) (andmap p (rest alox)))] ) interesting: p is the function to check the first element - but is also the argument so the weriting is highly similar for both cases… (andmap even? `(2 6)) = true

101 Das ultimative Lambda …
Der Fall andmap und ormap ;; ormap : (X -> boolean) (listof X) -> boolean ;; determines whether p holds for at least one item ;; on alox ;; (ormap p (list x x-n)) = ;; (or (p x-1) (or ... (p x-n))) (define (ormap p alox) (cond [(empty? alox) false] [else (or (p (first alox)) (ormap p (rest alox)))])) (ormap even? (list 5 1) = false

102 Das ultimative Lambda …
Der Fall andmap und ormap Nach unserem Rezept könnten wir in dem Fall abstrahieren, wenn nur and, or Funktionen wären … (define (ormap p alox) (cond [(empty? alox) false ] [else (or (p (first alox)) (ormap p (rest alox)))])) (define (andmap p alox) (cond [(empty? alox) true ] [else (and (p (first alox)) (andmap p (rest alox)))]))

103 Das ultimative Lambda …
Der Fall andmap und ormap (define (andormap bool-op init p alox) (cond [(empty? alox) init] [else (bool-op (p (first alox)) (andormap bool-op init p (rest alox)))])) (define (andmap3 p l) (andormap my-and true p l)) (define (ormap3 p l) (andormap my-or false p l)) Eigentlich haben wir ja bereits eine abstrakte Funktion, die wir wieder verwenden können, um andmap and ormap abzuleiten. Wir bräuchten andormap gar nicht.

104 Das ultimative Lambda …
;; fold : Y (X Y -> Y) (listof X) -> Y (define (fold init combine-op lst) (cond [(empty? lst) init] [else (combine-op (first lst) (fold init combine-op (rest lst)))])) (define (prod alon) (fold 1 * alon)) (define (sum alon) (fold 0 + alon)) (define (andmap4 p alox) (fold true my-and (map p alox))) (define (ormap4 p alox) (fold false my-or (map p alox))) map converts the list element-wise - using the function p fold does combine those using my-and/my-or

105 Überblick Ähnlichkeiten in Definitionen Funktionen sind Werte
Abstraktionen aus Beispielen entwerfen Abstraktionen mit Funktionen als Werten entwerfen Funktionen “ad hoc” definieren “pipes-and-filters” Organisation von Berechnungen

106 [Erinnerung: Die Rolle von Abstraktion]
Programmierer können die Wartungskosten senken, indem sie Programme richtig organisieren Erstes Prinzip: Passe die Struktur der Funktion an die Struktur der Daten an. Erleichtert Erweiterungen von Funktionen, wenn die Menge der möglichen Eingabedaten sich verändert Zweites Prinzip: Führe angemessene Abstraktionen ein Jede abstrahierte Funktion erzeugt einen eindeutigen Kontrollpunkt für mindestens zwei verschiedene Funktionen, oft für mehr Jetzt wo wir Funktionen höherer Ordnung kennen, ist es an der Zeit, das erste Prinzip nochmals zu betrachten …

107 Addition der Quadrate aller ungeraden Knoten eines Baums …
(define (sum-of-odd-squares alon) (cond [(empty? alon) 0] [(not (cons? alon)) (if (odd? alon) (square alon) 0)] [else (+ (sum-of-odd-squares (first alon)) (sum-of-odd-squares (rest alon)))])) Prozessstruktur folgt der Datenstruktur

108 Sammeln der geraden Fibonacci-Zahlen von 0..n
Prozessstruktur folgt der Datenstruktur 0 für n = 0 Fib(n) = für n = 1 Fib(n-1) + Fib(n-2) sonst ;; sammelt die geraden Fib(k) für k = [0..n] (define (even-fibs n) (local ((define (process k) (cond [(> k n) empty] [else (local ((define f (fib k))) [(even? f) (cons f (process (+ k 1)))] [else (process (+ k 1))]))]) )) (process 0)) ) even-fibs n - sammelt die geraden fibonacci-zahlen process k - checked, on fib(k) gerade ist und haengt das an die liste - sonst ignoriert es die fib(k) - undgeht auf den fall weiter mit element (k+1) (fib is not build in)

109 Prozessstruktur folgt der Datenstruktur
(define (sum-of-odd-squares tree) (cond ((empty? tree) 0) ((not (cons? tree)) (if (odd? tree) (square tree) 0)) (else (+ (sum-of-odd-squares (first tree)) (sum-of-odd-squares (rest tree)) ) Können Sie Ähnlichkeiten zwischen den beiden Prozeduren erkennen? (define (even-fibs n) (local ((define (process k) (cond [(> k n) empty] [else (local ((define f (fib k))) [(even? f) (cons f (process (+ k 1)))] [else (process (+ k 1))]))]))) (process 0)))

110 Fokus auf die Prozessphasen …
Eine abstraktere Beschreibung der Berechnung zeigt … sum-of-odd-squares: Zählt die Blätter eines Baumes auf Filtert sie, um die ungeraden zu erhalten Bearbeitet die ungeraden, indem sie quadriert werden Akkumuliert die Ergebnisse mittels +, Start mit 0 even-fibs: Zählt die ganzen Zahlen von 0 bis n auf Bearbeitet jede ganze Zahl, indem die Fibonaccizahl berechnet wird Filtert sie um die geraden zu bekommen Akkumuliert die Ergebnisse mittels cons, Start mit der leeren Liste

111 Fokus auf die Prozessphasen …
sum-of-odd-squares Aufzählen: Blätter Filtern: ungerade? map: square Akkumul.: +, 0 even-fibs Aufzählen: Zahlen map: fib Filtern: even? Akkumul.: cons, empty

112 Prozessstruktur folgt der Datenstruktur
Die Struktur des Prozessflusses geht bei dieser Strukturierung verloren… Es gibt keine klaren Teile, die zu den Elementen der Prozessflussdarstellung passen; sie sind vermischt (define (sum-of-odd-squares tree) (cond [(empty? tree) 0] [(not (cons? tree)) (if (odd? tree) (square tree) 0) ] [else (+ (sum-of-odd-squares (first tree)) (sum-of-odd-squares (rest tree)) ) ] enumeration accumulation processing filtering

113 Zerlegung in Prozessphasen
Ein Prozess kann häufig in Sequenzen von Schritten zerlegt werden, z.B.: Kompilierung Auffinden von gemeinsamen Wörtern in zwei Textdateien Häufige Arten von Schritten Aufzählen, filtern, bearbeiten, akkumulieren Erstelle eine Liste aller Blätter [aufzählen] Hole die ungeraden Knoten aus der Liste [filtern] Erstelle die Liste der Quadrate [bearbeiten] Addiere die Quadrate [akkumulieren] aufzaehlen - haengt von der datenstruktur ab (das ergebnis ist hier immer eine liste) filtern - kann man auf listen allgemein definieren (map) akkumulieren - kann man auf listen allgemein definieren (fold) bearbeiten - spezielle funktion

114 Ein Mehrzweck-Filter Abstraktion über den Test, indem das Prädikat als Parameter übergeben wird. Eine Vorlage für den Filter-Prozess (define (filter test alox) (cond [(empty? alox) empty] [(test (first alox)) (cons (first alox) (filter test (rest alox)))] [else (filter test (rest alox))])) filter-prozess allgemein - test als funktion/praedikat gebraucht (filter even? (list )) -> ( )

115 Ein Mehrzweck-Zusammenfalter
Abstraktion über den binären Operator (combine-op) zum Zusammenfalten, den Initialwert (init) und über die Liste, die zusammengefaltet wird (alox) Wir wissen nur, dass wir zusammenfalten, indem wir den binären Operator auf die Werte der Liste anwenden. ;; fold : Y (X Y -> Y) (listof X) -> Y (define (fold init combine-op lst) (cond [(empty? lst) init] [else (combine-op (first lst) (fold init combine-op (rest lst)))])) Eine Vorlage für den Akkumulations-Prozess

116 Datenstruktur bedingt Enumeratoren
Die Art, eine Datenstruktur zu durchlaufen, kann nicht unabhängig von der speziellen Datenstruktur sein … Aufzählung von ganzen Zahlen… (define (enumerate-interval lo hi) (cond [(> lo hi) empty] [else (cons lo (enumerate-interval (+ lo 1) hi))])) das resultat der enumeration ist hier (immer) eine liste (enumerate-interval 3 10) --> ( )

117 Datenstruktur bedingt Enumeratoren
Blätter aufzählen… (define (enumerate-tree tree) (cond [(empty? tree) empty] [(not (cons? tree)) (list tree)] [else (append (enumerate-tree (first tree)) (enumerate-tree (rest tree)))]) ) list -> produziert eine 1-elementige liste append (build-in) -> haengt 2 listen aneinander if x --> ((1 3) 2 (5 (4 6))), then (enumerate-tree x) --> ( )

118 sum-of-odd-squares als Komposition von Schritten
(define (sum-of-odd-squares tree) (fold (map square (filter odd? (enumerate-tree tree) ) [filter] [transduce] [accumulate] enumerate-tree -> takes a tree and makes a list out of it. filter -> filters all the odd-numbers out of that list map -> square those elements fold -> accumulates all the elements… enumeration accumulation (sum-of-odd-squares ‘((1 3) 2 (5 (4 6))) ) 35 processing filtering

119 Andere Schritt-basierte Prozesse
(define (even-fibs n) (fold cons empty (filter even? (map fib (enumerate-interval 0 n))))) (define (highest-programmer-salary records) (fold max (map salary (filter programmer? records)) ) even-fibs enumeration -> get all numbers from 0-n map -> calculates the fibonacci-numers for those numbers filter -> even fib-numbers fold -> gives the list of those even fib-numbers back highest-programmer salary enumerate -> we have some records (list of people with their profession) filter -> programmers from the records map -> find the programmers salary in the list of programmers fold -> find max of the saleries.

120 Vorteile der Zerlegung in Schritte
Unterstützung modularer Programmentwürfe Entwürfe sind aus relativ unabhängigen Teilen (Komponenten) zusammengesetzt Die meisten Schritte können als Mehrzweck-Prozeduren geschrieben werden, die wiederum als Vorlage für eine Vielzahl von Prozessen dienen können. Einfach zu schreiben und zu verstehen. Förderung modularen Entwurfs durch: Bibliothek mit Standard-Komponenten Eine konventionelle Schnittstelle für eine flexible Verbindung der Komponenten. Nachteil bei dieser Art von Entwürfen? Der größte Nachteil ist wahrscheinlich, dass Programme häufig nicht effizient sind bei den Beispielen (z.B. enumerate-tree) werden ja mehrfach Listen als Zwischenergebnisse produziert, an denen wir selbst nicht interessiert sind Modulare Konstruktion ist eine mächtige Strategie, um die Komplexität im Entwurf zu kontrollieren.


Herunterladen ppt "Grundlagen der Informatik I Thema 5: Abstraktion von Design"

Ähnliche Präsentationen


Google-Anzeigen