SEPP03-ParallelitaetdeklSprachen [Kompatibilitätsmodus]

Werbung
Software Engineering für moderne, parallele Plattformen
3. Parallelität in deklarativen Programmiersprachen
Dr. Victor Pankratius
Dr. Victor Pankratius, Dipl.Inform. Frank Otto
IPD Tichy – Lehrstuhl für Programmiersysteme
KIT – die Kooperation von Forschungszentrum Karlsruhe GmbH und Universität Karlsruhe (TH)
Agenda
In den nächsten Vorlesungen: Überblick über Parallelisierungsansätze
in verschiedenen Programmiersprachen
Zunächst Prinzipien aus deklarativen Programmiersprachen
(logische / funktionale Sprachen)
Motivation:
Verstehen, wie hier parallelisiert wird
Weiterer Grund: Moderne (imperative) Sprachen greifen z.T. Konzepte aus
mehreren unterschiedlichen Paradigmen auf. Wir wollen deren Ursprünge
verstehen.
Anschließend in nächsten Vorlesungen: Ausführlichere Betrachtung
ausgewählter Sprachen und Bibliotheken im Hinblick auf Parallelität
2
Dr. Victor Pankratius, Frank Otto
Logische Programmiersprachen
Zur Erinnerung…
Verwenden logische Aussagen zur Durchführung von Berechnungen
Axiome: Aussagen, die als wahr angenommen werden; werden zum
Beweis anderer wahrer Aussagen verwendet
Deklarativer Ansatz: In logischen Programmen wird nur Menge von
Axiomen festgelegt, nicht aber wie die Ableitung anderer Aussagen
genau zu erfolgen hat
Inferenzregeln bestimmen, wie Aussagen abgeleitet werden können
Eingaben für die Programme sind Anfragen (Behauptungen), für die
ein Beweiser versucht, anhand der Axiome und Inferenzregeln deren
Wahrheitswert zu bestimmen
Anwendungsbeispiele: Regelbasierte Systeme, Abfragesprachen in
Datenbanken
3
Dr. Victor Pankratius, Frank Otto
Logische Programmiersprachen
Zur Erinnerung… Beispiel: Prolog (1)
Verwendet Horn-Klauseln zur Darstellung von Aussagen
B :- A1, A2, … , An
Konsequenz
(Kopf, Goal)
Voraussetzung
(Antezedenz, Rumpf, Body)
Wenn Aussagen A1 UND A2 UND ..An wahr, dann auch B wahr
Drei Arten von Klauseln
Regeln: Rechte und linke Seite vorhanden
Fakten (Axiome): Rechte Seite leer, d.h. ohne Voraussetzung erfüllt
Anfragen: Linke Seite leer
4
Dr. Victor Pankratius, Frank Otto
Logische Programmiersprachen
Beispiel: Prolog (2)
Fakten:
mutter (Eva, Tina).
vater (Lars, Tina).
mutter (Eva, Andreas).
vater (Lars, Anreas).
Regeln:
elternteil (M, K)
elternteil (V, K)
vorfahr (X, Y)
vorfahr (X, Y)
? vorfahr(Eva, Tina).
? vorfahr(Tina, W).
Dr. Victor Pankratius, Frank Otto
Andreas
:− mutter (M, K).
:− vater (V, K).
:− elternteil (X, Y).
:− elternteil (X, Z), vorfahr (Z, Y).
Anfragen:
5
Lars
Eva
Tina
Logische Programmiersprachen
Beispiel: Datalog als DB-Abfragesprache (1)
Sei R Relation
A
B
1
2
3
4
•
Relation R mit Hilfe von Prädikat modellieren:
R(x,y) ist wahr, wenn (x,y) zu R gehört.
R(1,2) ist wahr. R(3,4) ist wahr. R(5,7) ist falsch.
•
Regeln der Form Head ← Body können für
Datenabfragen verwendet werden
Weiteres Beispiel:
Gegeben Relation Movie(title, year, length, inColor, studioName, producer)
abgekürzt: Movie(t, y, l, c, s, p)
Regel:
LongMovie(t,y) ← Movie(t, y, l, c, s, p) AND l ≥ 100
Definiert Menge aller „langen“ Filme, die mindestens 100 Minuten lang sind. Anders
formuliert: „LongMovie“ ist wahr, wenn es ein Tupel in „Movie“ gibt, so dass
irgendwelche Werte in den ersten zwei Komponenten existieren
eine dritte Komponente l existiert, die mindestens den Wert 100 hat
irgendwelche Werte in den Komponenten 4 bis 6 existieren
6
Dr. Victor Pankratius, Frank Otto
Logische Programmiersprachen
Beispiel: Datalog als DB-Abfragesprache (2)
Komplexeres Beispiel:
Finde Titel und Jahr aller Filme, die mindestens 100
Minuten lang sind und in die den Fox-Studios
produziert wurden.
Skizze in relationaler Algebra
πtitle, year
Selektiere Titel
und Jahr
Regeln:
„Film-Tupel mit mindestens 100 Minuten “
∩
W(t,y,l,c,s,p) ← Movie(t, y, l, c, s, p) AND l ≥ 100
Bilde Durchschnitt
„Film-Tupel mit Fox-Studios“
X(t,y,l,c,s,p) ← Movie(t, y, l, c, s, p) AND s = ‘Fox‘
„Film-Tupel mit mindestens 100 Minuten und Fox-Studios“
Y(t,y,l,c,s,p) ← W(t, y, l, c, s, p) AND X(t,y,l,c,s,p)
σlength
≥ 100
σstudioName=‘Fox‘
Wähle Tupel mit
length ≥ 100
Wähle Tupel mit
Studio Fox
Movie
Movie
„Selektiere Titel und Jahr aus Ergebnis Y“
Z(t,y) ← Y(t,y,l,c,s,p)
7
Dr. Victor Pankratius, Frank Otto
Vgl. auch GarciaMolina et al., Database
Systems
Parallelität in logischen Programmiersprachen
Überblick
Parallelität wird im Wesentlichen dafür verwendet, um den
Inferenzprozess zu beschleunigen
Zwei zentrale Ansätze
ODER-Parallelismus
UND-Parallelismus
8
Dr. Victor Pankratius, Frank Otto
Parallelität in logischen Programmiersprachen
ODER-Parallelismus
ODER-Parallelismus führt Klauseln parallel aus
Beispiel:
Regeln:
a(x):- b(x).
a(x):- c(x).
„wenn b(x) wahr, dann a(x) wahr“
ODER
„wenn c(x) wahr, dann a(x) wahr“
Anfrage: ? a(x)
ODER-Parallelismus führt parallel beide Klauseln a(x) aus
9
Dr. Victor Pankratius, Frank Otto
Parallelität in logischen Programmiersprachen
UND-Parallelismus
UND-Parallelismus
teilt die Berechnung in mehrere Fäden auf, die parallel je eine Aussage
evaluieren
Beispiel:
Anfage: ?- a(x), b(x), c(x)
Erzeugt drei Fäden, die jeweils a(x), b(x), c(x) getrennt evaluieren
10
Dr. Victor Pankratius, Frank Otto
Parallelität in logischen Programmiersprachen
Implizite Parallelisierung (1)
Parallelisierung mit UND- bzw. ODER-Parallelismus kann implizit
durch den Interpreter erfolgen
Keine expliziten Annotationen durch Programmierer nötig
Mehrere Granularitätsstufen denkbar, z.B.
Feingranular: Jede Regel startet neuen Faden
Grobgranular: Ein Faden arbeitet auf eigenen Teilbaum (Baumstruktur
entsteht bei Inferenz)
Beispiel-Implementierungen vgl. Skillikorn & Talia, Models and Languages for Parallel
Computation, ACM Comp Surv. 30(2), 1998
11
Dr. Victor Pankratius, Frank Otto
Parallelität in logischen Programmiersprachen
Implizite Parallelisierung (2)
Typische Probleme:
Sprachen sehr abstrakt
Verhaltens- und Performanzvorhersage schwierig
Spekulativer Parallelismus
Beim ODER-Parallelismus „spekuliert“ man darauf, dass die gesamte
parallele Arbeit notwendig ist. Wenn man nur am ersten möglichen
Ergebnis interessiert ist, kann man evtl. früher abbrechen. Das
Programmiermodell sieht aber generell nicht vor, dass Fäden miteinander
kommunizieren
12
Dr. Victor Pankratius, Frank Otto
Parallelität in logischen Programmiersprachen
Explizite Parallelisierung (1)
Explizite Parallelisierung ist möglich
Entwickler muss entsprechende Konstrukte, wie z.B. WächterBedingungen, einbauen
B :- W1, W2, …, Wn | A1, A2, … , An
Konsequenz
(Kopf, Goal)
Wächter
Voraussetzung
(Antezedenz, Rumpf)
Semantik: „B ist wahr, wenn Wächter wahr und Rumpf wahr“
Wächter kann als ein Test angesehen werden. Regel nur wirksam, wenn Test
erfolgreich
Wächter und Rumpf könnten parallel evaluiert werden (aber: Ergebnisse nur temporär, nur lokale
Änderungen, die rückgängig gemacht werden können)
sobald Test fehlschlägt, werden Klausel und temporäre Ergebnisse verworfen, sonst „commit“
13
Dr. Victor Pankratius, Frank Otto
Parallelität in logischen Programmiersprachen
Explizite Parallelisierung (2)
Beispiel-Implementierungen, die expliziten Parallelismus verwenden
PARLOG (Gregory 1987)
Concurrent Prolog (Shapiro 1986)
Delta-Prolog (Pereira, Nasr 1984)
14
Dr. Victor Pankratius, Frank Otto
Parallelität in logischen Programmiersprachen
Verschiedene andere Erweiterungen wurden vorgeschlagen (vgl.
Huntbach, Ringwood, Programming in Concurrent Logic Languages, IEEE Software,
Nov. 1995)
Aber: Explizite Konstrukte für Parallelität weichen vom ursprünglichen
Ziel logischer Sprachen ab, nur zu spezifizieren „was“ getan werden
soll, nicht „wie“
15
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Zur Erinnerung (1)…
Hauptprogramm ist eine Funktion
im mathematischen Sinne: Abbildung zwischen zwei Mengen (Werte aus
Definitionsbereich werden auf Werte im Wertebereich abgebildet)
Funktion erhält Eingabedaten und bildet sie auf Ausgabedaten ab
Kann weitere Funktionen aufrufen
Auch hier steht deklarativer Ansatz im Vordergrund („was“, nicht „wie“)
Typische zentrale Datenstruktur: Liste
16
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Zur Erinnerung (2)…
Reine funktionale Sprachen (pure functional languages)
Variablen nur Platzhalter; sie repräsentieren keine Speicherstelle
(d.h. „i=i+1“nicht möglich)
Keine Schleifen (sondern Rekursion)
Keine Seiteneffekte (z.B. innerhalb einer Funktion keine
Aktualisierung globaler Variablen möglich)
Anwendung von Funktion auf bestimmtes Argument liefert immer
das selbe Ergebnis.
Gegenbeispiel:
f(x)
{ return x+y;}
Sei y=1;
f(1) liefert 2
// zwischenzeitlich y++;
f(1) liefert 3
Aufruf von f(1) liefert nicht immer dasselbe Ergebnis
17
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Zur Erinnerung (3)…
Reine funktionale Sprachen (pure functional languages)
Referenzielle Transparenz: In einem Ausdruck A kann jeder beliebige
Teilausdruck T durch einen Teilausdruck T‘ gleichen Wertes ersetzt
werden, ohne dass sich der Wert von A verändert
Beispiel: Referenzielle Transparenz
ab+ab
c + a b, a b + c, c + c, 2*c, 2*ab
c=ab
• Bei Sprachen mit Seiteneffekten ist keine referenzielle Transparenz
vorhanden. Beispielsweise kann ab+ab ≠ 2*c sein, wenn
zwischendurch a++ ausgeführt wird
• Mit referenzieller Transparenz wird die Programmanalyse vereinfacht.
Ein Ausdruck hängt nur von Teilausdrücken ab, nicht auch noch von
der Reihenfolge der Auswertung oder von Seiteneffekten.
18
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Zur Erinnerung (4)…
Unreine funktionale Sprachen
(impure functional languages)
Lockern einige der Einschränkungen auf
Erlauben z.B. Seiteneffekte (die z.B. durch Benutzerinteraktion, Art der Ein/Ausgabe)
19
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Zur Erinnerung (5)… Beispiele in Scheme
(* 3 7)
„*“ ist eine Funktion, die zwei Parameter benötigt; evaluiert zu 21
(DEFINE (fac n)
allgemein: (define (<name> <formale Parameter> <Rumpf>))
(IF (= n 0)
1
(* n (fac (- n 1)))
))
define (abs
(cond ((>
((=
((<
20
x)
x 0) x)
x 0) 0)
x 0) (- x))))
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Zur Erinnerung (6)… Beispiele in Scheme
(define x (cons 1 2))
(car x) liefert 1
(cdr x) liefert 2
(list 1 2 3) ist äquivalent zu (cons 1(cons 2(cons 3 nil)))
(define (length items)
(if (null? items)
0
(+ 1 (length (cdr items)))))
Länge einer leeren Liste ist 0
sonst 1+ Länge des cdr
(define l (list (1 2 3 4))
(length l)
liefert 4
(lambda (x) (+ x x))
evaluiert zu einer Funktion (ohne Namen) mit formalem Parameter x, die
auf Folgeargumente angewendet werden kann (Parameter wird entsprechend gebunden)
((lambda (x) (+ x x)) 4)
liefert 8
vgl. spätere Vorlesungen: delegates in C#, Vorschlag für „<>(…)“ Operator in C++0x
21
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Zur Erinnerung (7)…
Funktionen höherer Ordnung
Argument oder Ergebnis einer Funktion kann selbst eine Funktion sein
Beispiel:
• Apply wendet Funktion an
(apply + '(1 2))
(+ 1 2)
Gleicher Effekt, wie wenn man (+ 1 2) direkt aufgerufen hätte
• Map wendet Funktion auf alle Elemente einer Liste an
(map (abs (list 1 -2 3))) (1 2 3)
→ kann parallel ausgeführt werden; für Datenparallelismus gut geeignet SIMD
→ Verwendung beim MapReduce-Ansatz für verteilten und gemeinsamen Speicher
22
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Evaluierung kann unterschiedlich erfolgen
Alle Argumente einer Funktion werden evaluiert, bevor die Funktion selbst
evaluiert wird (z.B. Hope, OPAL)
Argumente werden nur evaluiert, wenn sie benötigt werden „lazy
evaluation“ (z.B. Haskell, pH, Id)
Einfache Argumente (z.B. Integer) werden vor der Funktion ausgewertet,
aber komplexe Argumente (z.B. Listen, rekursive Datenstrukturen) erst bei
Bedarf
23
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Ansätze für Parallelismus (1)
Reine funktionale Sprachen haben vorteihafte Eigenschaften für
Parallelisierung
Keine Seiteneffekte
Anwendung von Funktion auf bestimmtes Argument liefert immer dasselbe
Ergebnis
Reihenfolge der Funktionsauswertung nicht festgelegt
Semantik über Abbildung von Ein- auf Ausgabedaten definiert
Jedes sequenzielle Programm wird für eine bestimmte Eingabe auch bei
paralleler Ausführung dieselben Ergebnisse liefern und auch unter den gleichen
Bedingungen terminieren.
24
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Ansätze für Parallelismus (2)
Implikationen
Parallele funktionale Programme könnten auch auf sequenziellen
Rechnern entwickelt und getestet werden
Das Ergebnis ist unabhängig vom dynamischen Scheduling von Aufgaben
Verklemmungen sind nicht möglich, außer in Situationen in denen das
sequenzielle Programm auch versagen würde (z.B. bei zyklischen
Abhängigkeiten)
25
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Ansätze für Parallelismus (3)
Ansätze für Parallelismus
Implizite Partitionierung
Interpreter/Compiler entscheidet, welche Aufgaben parallel bearbeitet werden
sollen
Explizite Partitionierung
Entwickler entscheidet, welche Ausdrücke parallel ausgeführt werden sollen
In beiden Fällen noch Auswahl zwischen
Statisch: Anzahl der zu erstellenden Aufgaben fix
Dynamisch: Aufgaben werden in Abhängigkeit von anderen Faktoren, wie z.B.
aktuelle Auslastung, erstellt
26
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Ansätze für Parallelismus (5)
Impliziter Parallelismus – Beispiele für Realisierung
„Serial Combinators“
Der Interpreter/Compiler fügt ohne Wissen des Entwicklers PseudoFunktionen für die Parallelisierung ein
Beispiel:
fun n= if n<= 1 then 1
else 1+fun(n-1)+fun(n-2)
27
Dr. Victor Pankratius, Frank Otto
fun n=
(demand n
(spawn ((n1 (fun (n-1)))
(n2 (fun (n-2))))
wait (n1 n2)
(n1+n2+1))))
Parallelität in funktionalen Programmiersprachen
Ansätze für Parallelismus (6)
Expliziter Parallelismus
Annotations-Ansatz
Annotationen werden vom Entwickler explizit eingefügt
Grobgranular (z.B. Annotation ob Funktion überhaupt parallel ausgeführt
werden soll) oder feingranular
Scheduling-Konstrukte (Prozess-Erstellung/Zerstörung, Ort der Ausführung,
etc.)
Beispiel: exp $on left($self)
führe exp auf Prozessor aus, der sich „links“ vom aktuellen Prozessor befindet
28
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Skelette (1)
Algorithmische Skelette
Von Cole 1989 benannt
Idee: Erfasse häufig gebrauchte Verarbeitungsmuster, wie z.B.
teile-und-herrsche, Fließbandverarbeitung oder „worker farm“, in
Funktionen höherer Ordnung
Diese „Schablonen“ können dann vom Entwickler bei Bedarf
instanziiert werden
Parallelität kann in einem Skelett „gekapselt“ werden
Wiederverwendung auf unterschiedlichen Architekturen: Nur
plattformabhängiger Teil muss jeweils neu implementiert werden
Ähnlich zu Entwurfsmustern
29
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Skelette (2)
Algorithmische Skelette
Pseudocode-Beispiel für ein vereinfachtes Teile-und-herrsche-Skelett
divCon divisible split join f L =
if divisible then
join (parmap f (split L))
else
f L
• Benutzer definiert Argumente:
•
•
•
•
divisible (wahr/falsch)
Liste L
Funktionen split/join zum Aufteilen/Zusammenführen von L
Funktion f, die auf „Basisfall“ angewendet werden soll
• Parmap ist parallele „map“-Funktion, die f auf jedes Teilproblem
angewendet
30
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Skelette (3)
Homomorphe Skelette
Basieren auf bestimmten Datentypen, z.B. Listen, Bäume, Graphen
Beispiel: Liste
i-tes Element
• Ersetze für folgende
f
f
g
f
f
g
f
f
g
f
f
Berechnungen f und g so:
g
• Summe
f = id,
g=+
• Maximum
f = id,
g = binäres Maximum
• Länge
f = K1,
g=+
Zeit
g
g
g
(K1 ist Funktion, die immer 1 zurückliefert)
• Sort
31
Dr. Victor Pankratius, Frank Otto
f = id,
g = merge
Parallelität in funktionalen Programmiersprachen
Futures (1)
Futures
Konzept erstmals in Multilisp verwendet (Halstead, 1985)
Ein „Future“-Konstrukt ist ein Platzhalter für einen Wert
Am Anfang ist kein Wert bestimmt
Wenn initial das Konstrukt
(future X)
aufgerufen wird, liefert es dem Aufrufer einen Platzhalter und startet
gleichzeitig einen Prozess, der X evaluiert
32
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Futures (2)
Futures (fortgesetzt)
Wenn der Wert bestimmt ist, wird der Platzhalter durch den ermittelten
Wert ersetzt (Details gleich)
Eine Operation (z.B. Addition) wird suspendiert, falls sie den konkreten
Wert benötigt, bevor er ermittelt ist
Beispiel:
(+ (future A) (future B))
„+“ wird suspendiert, bis die Werte vorhanden sind
Viele andere Operationen (z.B. Zuweisungen, Parameterübergabe,
Rückgabe von Funktionen, Einfügen in Datenstrukturen) können mit dem
Platzhalter arbeiten und benötigen den konkreten Wert nicht
33
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Futures (3)
Futures (fortgesetzt)
Futures erlauben eine Fortsetzung des Kontrollflusses über die Stelle
hinaus, bei der auf einen Wert gewartet werden müsste
Synchronisation geschieht implizit und ist für Entwickler nicht sichtbar
Das Future-Konzept begegnet uns später auch bei Java und .NET
34
Dr. Victor Pankratius, Frank Otto
Parallelität in funktionalen Programmiersprachen
Futures (4)
Einige Details zur internen Funktionsweise eines
Future-Konstrukts
Future besteht aus
(Wert W, Warteschlange S, booleschen Wert B, Sperre Sp)
Initial ist B=false, S: leer
Bei Zugriff auf Future führe folgendes atomar aus
Wenn B=true dann liefere W
Ansonsten suspendiere Aufrufer und füge ihn zu S hinzu
(vermeidet „busy waiting“)
Wenn W ermittelt
Platzhalter wird zu Referenz auf W
Benachrichtige bzw. reaktiviere Aufrufer in Warteschlage S
35
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Stromverarbeitung (1)
Stromverarbeitung
Mit funktionalem Ansatz leicht umzusetzen
Beispielanwendungen: Datenströme (Datenstrom-Management-systeme),
Signale, Multimedia, Fließbänder
Konzepte begegnen uns in ähnlicher Form bei „Stream“Programmiersprachen im Multicore-Kontext
Zunächst einige motivierende Beispiele in Scheme
(define (generator low high)
(if (> low high)
nil
(cons low (generator (+ low 1) high))))
Aufruf: (generator 2 7)
Ergebnis: (2 3 4 5 6 7)
36
Dr. Victor Pankratius, Frank Otto
generator
Wenn low<high,
füge low zur Liste
hinzu und rufe
rekursiv generator
mit [low+1, high]
auf
Funktionale Programmiersprachen
Stromverarbeitung (2)
Motivierende Beispiele (Fortsetzung)
Filter
predicate
(define (filter predicate sequence)
(cond ((null? sequence) nil)
((predicate (car sequence))
(cons (car sequence)
(filter predicate (cdr sequence))))
(else (filter predicate (cdr sequence)))))
Fallunterscheidung:
-
Wenn Sequenz leer, gib nil zurück
-
Prädikat für erstes Element wahr, dann nimm Element in Liste auf und wende
Filter mit Prädikat auf Rest der Liste an
-
Prädikat nicht zutreffend: Wende Filter auf Rest der Liste an
Aufruf: (filter odd? (generator 1 5))
Ergebnis: (1 3 5)
37
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Stromverarbeitung (3)
Motivierende Beispiele (Fortsetzung)
Accumulate
op initial
(define (accumulate op initial sequence)
(if (null? sequence)
initial
(op (car sequence)
(accumulate op initial (cdr sequence)))))
Wenn Sequenz leer, gib initialen Wert zurück, ansonsten
wende Operator auf erstes Element und dem akkumulierten Rest der Liste an
Beispiel-Aufrufe
Vgl. auch
Reduktion
38
(accumulate + 0 (generator 1 5))
15
(accumulate * 1 (generator 1 5))
120
(accumulate cons nil (generator 1 5))
(1 2 3 4 5)
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Stromverarbeitung (4)
Motivierende Beispiele (Fortsetzung)
Kombiniere Operatoren
(define (sum-odd-quares n)
(accumulate + 0
(map square
(filter odd?
(generator 0 n)))))
generator
filter odd?
square
accumulate
(define (even-fibs n)
(accumulate cons nil
(filter even?
(map fib
(generator 0 n)))))
generator
39
map fib
Dr. Victor Pankratius, Frank Otto
filter even
accumulate
Funktionale Programmiersprachen
Stromverarbeitung (5)
Motivierende Beispiele (Fortsetzung)
Kombiniere Operatoren
Liste von
Listen
(define (salary-of-highest-paid-programmer records)
(accumulate max
0
(map salary
(filter programmer? records))))
„Selektor“: Liefert
Betrag des Gehaltes
aus Datensatz
40
Dr. Victor Pankratius, Frank Otto
Überprüft, ob
bestimmter Datensatz
der eines
Programmierers ist
Funktionale Programmiersprachen
Stromverarbeitung (6)
Ströme
Probleme mit bisherigem Ansatz:
Strom ist als Liste implementiert
Datenstruktur endlich
Hoher Speicherverbrauch / Rechenzeit bei langen Strömen
Generiert zunächst das
gesamte Intervall
(define (sum-primes a b)
(accumulate + 0
(filter prime? (generator a b))))
Erst wenn Generator fertig ist, wird das Prädikat
angewendet; es wird eine neue Liste mit Ergebnissen
generiert, die an accumulate übergeben wird. Ein solch
großes Zwischenergebnis wäre bei inkrementeller
Enumeration und Weitergabe nicht notwendig
41
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Stromverarbeitung (7)
Ströme
In Realität: Datenstrom kann sich mit der Zeit ändern
Stromlämge kann potenziell unendlich sein
Neue Datenstruktur “Datenstrom”
Idee: Konstruiere einen Datenstrom nur partiell und übergebe zunächst nur
denjenigen Teil, der gerade benötigt wird, an einen Konsumenten
Verzögerte Auswertung (“lazy evaluation”)
Wenn Konsument auf einen Teil zugreifen will, der noch nicht existiert, dann
werden automatisch gerade so viele neue Elemente produziert, wie vom
Konsumenten benötigt.
Für Konsumenten: Illusion, dass kompletter Strom existiert
Programm wird so geschrieben, als ob komplette Sequenz vollständig
vorhanden wäre
42
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Stromverarbeitung (8)
Vergleich
• Liste
…
1
2
3
• Strom
1
43
Dr. Victor Pankratius, Frank Otto
2
3
4
„Versprechen“,
das nächste
Element zu
liefern, sobald es
gebraucht wird
Funktionale Programmiersprachen
Stromverarbeitung (9)
Operationen auf Datenstrom
Analog zu Operatoren auf Listen
z.B. cons-stream, stream-null?, the-empty-stream, stream-car,
stream-cdr, stream-map, stream-generate
Jedoch geringfügig modifiziert, damit verzögerte Verarbeitung funktioniert
44
Dr. Victor Pankratius, Frank Otto
Funktionale Programmiersprachen
Stromverarbeitung (10)
Die verzögerte Auswertung
kann auch mit expliziten Konstrukten kontrolliert werden
(delay exp) evaluiert Ausdruck exp nicht sofort, sondern
“verspricht” ihn irgendwann in der Zukunft zu evaluieren
force bekommt einen mit delay verzögerten Ausdruck als Argument
und evaluiert ihn (force “zwingt” das Delay-Konstrukt, das
“Versprechen” einzulösen).
(cons-stream <a> <b>)
(cons <a> (delay <b>))
ist äquivalent zu
Beispiel zur Konstruktion eines Stroms mit Hilfe von
Paaren. Im cdr werden nicht von Anfang an alle
übrigen Werte abgelegt, sondern nur Versprechen zur
Evaluation bei Bedarf
(define (stream-car stream) (car stream))
(define (stream-cdr stream) (force (cdr stream)))
45
Dr. Victor Pankratius, Frank Otto
stream-cdr selektiert cdr des
Paares und erzwingt seine
Evaluation, um Rest des Stroms zu
bekommen
Funktionale Programmiersprachen
Stromverarbeitung (11)
Weitere Beispiele
Datenstrom mit unendlich vielen Elementen
(define (integers-starting-from n)
(cons-stream n (integers-starting-from (+ n 1))))
Addition von Datenströmen
(define (add-streams s1 s2)
(stream-map + s1 s2))
Mehr dazu: Vgl. auch Abelson et al., Structure and Interpretation of Computer Programs MIT Press, 1984
Volltext online: http://mitpress.mit.edu/sicp/full-text/book/book.html
46
Dr. Victor Pankratius, Frank Otto
Herunterladen