WS 2013/2014. Robert Giegerich. 8. Januar 2014

Ähnliche Dokumente
Lazy Pattern Matching. 10. Dezember 2014

5. Januar Universität Bielefeld AG Praktische Informatik. Programmieren in Haskell. Stefan Janssen. Abstrakte Datentypen.

WS 2011/2012. Robert Giegerich. 19. Dezember 2012

Programmieren in Haskell Abstrakte Datentypen

Programmieren in Haskell. Abstrakte Datentypen

Programmieren in Haskell Programmiermethodik

12. Dynamische Datenstrukturen

B2.1 Abstrakte Datentypen

Programmierung 1 (Wintersemester 2015/16) Wiederholungstutorium Lösungsblatt 15 (Linearer Speicher, Listen, Bäume)

Wiederholung: Zusammenfassung Felder. Algorithmen und Datenstrukturen (für ET/IT) Definition Abstrakter Datentyp. Programm heute

Einführung in die funktionale Programmierung

ALP II Dynamische Datenmengen Datenabstraktion

Haskell Seminar Abstrakte Datentypen. Nils Bardenhagen ms2725

WS 2011/2012. Robert Giegerich Dezember 2013

WS 2011/2012. RobertGiegerich. November 12, 2013

WS 2011/2012. RobertGiegerich. November 12, 2013

Einführung in die Programmiertechnik

Einführung in Haskell

2.3 Spezifikation von Abstrakten Datentypen

16. Dynamische Datenstrukturen

Abstrakter Datentyp (ADT): Besteht aus einer Menge von Objekten, sowie Operationen, die auf diesen Objekten wirken.

SS10 Algorithmen und Datenstrukturen 2. Kapitel Fundamentale Datentypen und Datenstrukturen

Alexander Nutz. 3. Dezember 2007

Programmieren in Haskell

ALP I. Funktionale Programmierung

Programmieren in Haskell Einstieg in Haskell

Counting - Sort [ [ ] [ [ ] 1. SS 2008 Datenstrukturen und Algorithmen Sortieren in linearer Zeit

Abstrakte Datentypen und Datenstrukturen

Algorithmen und Programmieren 1 Funktionale Programmierung - Musterlösung zur Übungsklausur -

Funktionale Programmierung. Das Funktionale Quiz. Das Funktionale Quiz. Das Funktionale Quiz

13 Abstrakte Datentypen

Musterlösung zur 2. Aufgabe der 4. Übung

Algorithmen und Programmierung

Graphdurchmusterung, Breiten- und Tiefensuche

Grundlagen der Informatik. Prof. Dr. Stefan Enderle NTA Isny

13. Dynamische Datenstrukturen

Paradigmen der Programmierung

Workshop Einführung in die Sprache Haskell

Übung Algorithmen und Datenstrukturen

Programmieren in Haskell. Stefan Janssen. Strukturelle Rekursion. Universität Bielefeld AG Praktische Informatik. 10.

Abstrakte Datentypen und deren Implementierung in Python

Algorithmen und Programmieren 1 Funktionale Programmierung - Musterlösung zu Übung 9 -

Algorithmen und Datenstrukturen

10 Abstrakte Datentypen

Informatik II, SS 2014

Abstrakte Datentypen I

Kapitel 3: Eine einfache Programmiersprache. Programmieren in Haskell 1

HASKELL KAPITEL 2.1. Notationen: Currying und das Lambda-Kalkül

WS 2011/2012. Robert Giegerich. October 17, 2012

Frage, Fragen und nochmals Fragen

WS 2011/2012. Georg Sauthoff 1. October 18, 2011

5.3 Doppelt verkettete Listen

Aufgabe 1 Basiswissen zur Vorlesung (8 Punkte)

ALP II Dynamische Datenmengen Datenabstraktion (Teil 2)

Einfügen immer nur am Kopf der Liste Löschen auch nur an einem Ende (2 Möglichkeiten!)

Programmieren in Haskell

Stack. Queue. pop() liefert zuletzt auf den Stack gelegtes Element und löscht es push( X ) legt ein Element X auf den Stack

WS 2013/2014. Robert Giegerich. 11. Dezember 2013

Was bisher geschah. deklarative Programmierung. funktionale Programmierung (Haskell):

Grundlegende Datentypen

11. Elementare Datenstrukturen

Organisatorisches. Algorithmen und Datenstrukturen (für ET/IT) Programm heute. Definition Feld. Definition Feld

Einführung in die Informatik 2

Grundlegende Datentypen

Gliederung. Funktionale Programmierung. Pattern matching in Haskell. Pattern matching in ERLANG. Materialien zur Vorlesung

Praktikum Funktionale Programmierung Teil 3: Abstrakte Maschinen Mark 1 und Mark 2

Funktionale Programmierung

Tutoraufgabe 1 (Implementierung eines ADTs):

1 Abstrakte Datentypen

Gliederung. Algorithmen und Datenstrukturen I. Binäre Bäume: ADT Heap. inäre Bäume: ADT Heap. Abstrakte Datentypen IV. D. Rösner

Funktionale Programmierung Grundlegende Datentypen

Einstieg in die Informatik mit Java

Datenstrukturen und Algorithmen. Vorlesung 8

Programmieren in Haskell

Gliederung. Algorithmen und Datenstrukturen Einführung. Abstrakte Datentypen. Abstrakte Datentypen in Haskell. Abstrakte Datentypen (ADT) I

8 Elementare Datenstrukturen

Stacks, Queues & Bags. Datenstrukturen. Pushdown/Popup Stack. Ferd van Odenhoven. 19. September 2012

Listen. M. Jakob. Gymnasium Pegnitz. 20. September Hinführung: Wartenschlangen. Grundprinzip von Listen Rekursion

Funktionale Programmierung mit Haskell

Spezielle Datenstrukturen

Vorlesung Datenstrukturen

Programmieren in Haskell

Funktionale Programmierung ALP I. Algebraische Datentypen und Abstrakte Datentypen. SS 2013 Prof. Dr. Margarita Esponda. Prof. Dr.

Nachtrag: Vergleich der Implementierungen von Stack

Programmieren in Haskell

Algorithmen und Datenstrukturen (für ET/IT)

Einfache Liste: Ein Stapel (Stack) Ansatz. Schaubild. Vorlesung 1. Handout S. 2. Die einfachste Form einer Liste ist ein Stapel (stack).

Listen. M. Jakob. 20. September Gymnasium Pegnitz

Übung zur Vorlesung Programmierung

Algorithmen und Datenstrukturen I

Algorithmen und Datenstrukturen (für ET/IT)

Praktische Informatik 3: Funktionale Programmierung Vorlesung 4 vom : Typvariablen und Polymorphie

Algorithmen und Datenstrukturen

Algorithmen und Datenstrukturen (für ET/IT)

VL06: Haskell (Funktionen höherer Ordnung, Currying)

Programmieren in Haskell Das Haskell Typsystem

Haskell for Hackers... or why functional programming matters

Programm heute. Algorithmen und Datenstrukturen (für ET/IT) Feld als sequentielle Liste. Definition Feld

Programmierkurs II. Typsynonyme & algebraische Datentypen

Gliederung. Algorithmen und Datenstrukturen I. Abstrakte Datentypen. Abstrakte Datentypen. bstrakte Datentypen. D. Rösner

Transkript:

in in WS 2013/2014 Robert AG Praktische Informatik 8. Januar 2014

Themen-Vorschau in : as-s und lazy s Client-Server-Programmierung Module in Holy Lists

in in matching (alias Mustervergleich) kennen wir bereits aus Funktionsgleichungen aus case-ausdrücken Ein (Muster) ist ein Ausdruck bestehend aus Variablen, der anonymen Variablen _ ( Joker ) Konstruktoren aller Zahlen, Characters... also keine Funktionen, die nicht Konstruktoren sind... und keine Variable mehrfach, ausgenommen _

Ergonomie des in matching leistet zweierlei Fallunterscheidung in übersichtlicher Form Nutzung der Reihenfolge bei überlappenden Mustern Bindung von Muster-Variablen an Substrukturen des geprüften Werts 1 leaves ( Leaf a) = [a] 2 leaves ( Br ( Leaf a) y) = a : leaves y 3 leaves (Br (Br x y) z) = leaves (Br x (Br y z)) in

As-s 1 Manchmal will man Muster verwenden und einen Namen für den (unzerlegten) Argumentwert haben 2 suffixes (x:xs) = (x:xs ): suffixes xs erzeugt eine überflüssige (:) Operation, vergeudet Zeit und verschwendet Platz. Besser mit as-pattern suffixes z@(x:xs) = z: suffixes xs Nun ist das schon vorhandene x:xs unter dem Namen z zu haben. in Nebenbei: Welche Komplexität hat die Berechnung von suffixes x? Und welche die Ausgabe des Ergebnisses?

und Auswertungreihenfolge in Vergleich eines Musters mit einem Wert erfordert, dass der Wert ausgerechnet wird, aber nur soweit es für den Vergleich nötig ist (Laziness) Verwendung von Mustern hat also Auswirkung auf die Berechenungsreihenfolge

Abweisbare und nicht abweisbare Muster a.k.a. refutable versus irrefutable patterns Ein Muster ohne Konstruktoren (also x, _ ) passt auf jedem Wert und heißt unabweisbar Ein Muster mit Konstruktoren heißt abweisbar Ist eine Funktion mit unabweisbaren Mustern definiert, ist sie immer ein Redex. Beispiele: 1 square x = x * x 2 double f = f. f 3 five _ = 5 NB: Man kann alle Funktionen auf der linken Seite mit unabweisbaren Mustern schreiben, wenn man stattdessen case-ausdrücke auf der rechten Seite verwendet. in

und Terminierung (1) Erinnerung: Die Auswertungsstrategie in einer funktionalen Sprache hat keinen Einfluss auf den ggf. berechneten Wert, wohl aber auf die Terminierung der Berechnung. Ein Mustervergleich kann positive oder negativ ausgehen, oder divergieren Alle Muster in einer Gleichung werde top-down, links-rechts verglichen Geht ein Vergleich negativ aus, ist die aktuelle Gleichung nicht anwendbar, und es wird die nachfolgende Gleichung versucht Ruft der Vergleich eine nicht-endende Berechnung hervor, divergiert er und das Ergebnis der Gesamtrechnung ist undefiniert. Passen alle Muster einer Gleichung, wird sie angewendet in

Bottom in Hier eine Definition des undefinierten Werts 1 > bot = bot bot, ausgesprochen bottom ist der gänzlich undefinierte Wert. Seine Berechnung terminiert nicht, und produziert auch keinen Konstruktor. Was wäre ein teilweise undefinierter Wert? Nebenbei: Welchen Typ hat bot?

und Terminierung (2) in Vergleiche 1 take 0 _ = [] 2 take _ [] = [] 3 take n (x:xs) = x : take (n -1) xs und mit vertauschten Zeilen 1 take _ [] = [] 2 take 0 _ = [] 3 take n (x:xs) = x : take (n -1) xs Einen Unterschied gibt es nur, wenn eines der Argumente undefiniert ist...

Unterschiedliches Terminierungsverhalten in 1 take 0 bot ==> [] 2 take 0 bot = = > undefiniert 3 4 take bot [] = = > undefiniert 5 take bot [] ==> [] Der Prelude implementiert die Version take.

Lazy s in a.k.a. Muster mit verzögertem Vergleich Problem: Man möchte eine Funktion mit einem Muster definieren und zwar für ein Argument, das durch die Anwendung der Funktion erst berechnet wird Zwar steht fest, dass das Muster erfüllt sein wird, jedoch zum Zeitpunkt der Funktionsaufrufs muss für den Mustervergleich die gleiche Funktion wieder aufgerufen werden... Wo kommt so etwas überhaupt vor?

Client-Server-Anwendungen Client-Server-Anwendungen sind parallel laufende Programme, die über Ströme kommunizieren Nachrichten an einander Requests und Responses werden wechselseitig erzeugt und gelesen Sie werden in Strömen (streams) verwaltet. Ströme sind in einfach lazy Listen, in andern Sprachen weden sie als spezieller Datentyp zur Verfügung gestellt. in

Zahlen raten als client-server Programm (1) Ratespiel: Finde eine Zahl n aus dem Intervall [0..N] durch Fragen der Art Ist n z? mit geschickt gewählten Vergleichszahlen z. Rollenverteilung: Client: Erzeugt Fragen und merkt sich das Ergebnis. Seine Strategie: Intervall-Halbierung; sie führt garantiert in O(log(N)) Schritten zum Ziel. Server: Kennt n und beantwortet Fragen Ist n z? mit True oder False. Der Server antwortet wahrheitsgemäß. (In einer interesssanteren Variante des Spiels darf er einmal lügen.) Kommunikation zwischen Client und Server über zwei Ströme: questions und answers. in

Zahlen raten als client-server Programm (2) Eine Frage hat die Form (l,g,u) und bedeutet ausführlich: Ich weiß dass n [l..u] und frage ob n g. g ist dabei die Intervallmitte, und wird, je nach Antwort, für die nächste Frage ins linke oder rechte Halbintervall verlegt. Das Ende erkennt der Client daran, dass das Intervall sich nicht mehr ändert. 1 > minn = 0 2 > maxn = 1024 3 > t a k e d i f f [ ] = [ ] 4 > t a k e d i f f [ x ] = [ x ] in 5 > t a k e d i f f ( x : y : xs ) = x : i f y == x then [ ] e l s e t a k e d i f f ( y : xs ) takediff nimmt den Präfix einer Liste bis zur ersten Wiederholung

Zahlen raten als client-server Programm (2) 1 Wir definieren die Prozesse ask und reply, die über die Listen questions und answers kommunizieren. in 2 > g u e s s n = t a k e d i f f (map ( \ (_, g, _) > g ) q u e s t i o n s ) where 3 > a nswers = r e p l y q u e s t i o n s 4 > r e p l y ( (_, g, _ ) : qs ) = ( n <= g ) : r e p l y qs 5 6 > q u e s t i o n s = ask i n i t i a l answers where 7 > i n i t i a l = ( minn, (maxn + minn ) div 2, maxn) 8 > ask ( l, g, u ) ( a : as ) = ( l, g, u ) : ask newguess as where 9 > newguess = i f a then ( l, ( l + g ) div 2, g ) 10 > e l s e ( g+1, ( g+1 + u ) div 2, u ) Diese Version funktioniert nicht warum?

Verklemmung! Der Aufruf von ask (l,g,u) (a:as) stellt eine Frage ((l,g,u)), die von reply beantwortet wird (a) fragt jedoch in seinem rechten Muster bereits nach dieser Antwort ((a:as)), um aus ihr die nächste Frage (newguess) zu generieren Das pattern matching beim Aufruf von ask kommt zu frueh: es verlangt ein Ergbnis von reply, bevor der gegebene Aufruf von ask das erste Element von questions generiert hat. reply seinerseits verlangt ein Ergebnis von ask... in

Zahlen raten als client-server Programm (3) 1 Vermeidung des übereifrigen Musters: 2 > a nswers = r e p l y q u e s t i o n s 3 > r e p l y ( (_, g, _ ) : qs ) = ( n <= g ) : r e p l y qs 4 in 5 > q u e s t i o n s = ask i n i t i a l answers where 6 > i n i t i a l = ( minn, (maxn + minn ) div 2, maxn) 7 > ask ( l, g, u ) as = ( l, g, u ) : ask newguess ( t a i l as ) where 8 > a = head as 9 > newguess = i f a then ( l, ( l + g ) div 2, g ) 10 > e l s e ( g+1, ( g+1 + u ) div 2, u ) ask kann nun starten, ohne zuerst nach der Zerlegung der answers-liste as zu fragen. Also kann die Frage ausgegeben und daraus die erste Antwort erzeugt werden, bevor mit head as nach ihr gefragt wird

Zahlen raten als client-server Programm (4) Der gleiche Effekt mit lazy pattern ~(a:as) 1 Otherwise, the same as s o l u t i o n ( 1 ) 2 in 3 > guess n = t a k e d i f f (map ( \ (_, g, _) > g ) q u e s t i o n s ) where 4 5 > a nswers = r e p l y q u e s t i o n s 6 > r e p l y ( (_, g, _ ) : qs ) = ( n <= g ) : r e p l y qs 7 8 > q u e s t i o n s = ask i n i t i a l answers where 9 > i n i t i a l = ( minn, (maxn + minn ) div 2, maxn) 10 > ask ( l, g, u ) ~( a : as ) = ( l, g, u ) : ask newguess as where 11 > newguess = i f a then ( l, ( l + g ) div 2, g ) 12 > e l s e ( g+1, ( g+1 + u ) div 2, u ) der lazy pattern ~(a:as) wird beim Aufruf als erfüllt angenommen erst wenn a oder as gebraucht wird, findet der Abgleich statt.

Lazy s in, ~(...) sind unabweisbar. Ihre Überprüfung findet nur statt, wenn eine Komponente daraus gebraucht wird Scheitert sie, bricht die Rechnung ab. Eine weitere Mustergleichung nach einer Gleichung mit einem lazy macht also keinen Sinn.

Top level pattern binding: immer lazy Werden Muster nicht auf Argumentposition, sondern in der Definition von Konstanten benutzt, sind sie automatisch lazy: fibp@(1:tfibp) = 1:1:[a+b (a,b) <- zip fibp tfibp ] Hier haben wir einen as-, der eine konstante, unendliche Liste definiert,...... die Liste der Fibonacci-Zahlen. Das Muster bindet die Gesamtliste an den Namen fibp, zugleich ihren tail an den Namen tfibp die linke Seite fragt, ob die Liste nicht leer ist, aber dank der laziness des Musters wird dies erst geprüft, wenn tfibp rechts benutzt wird Vergleiche: let a@(1:as) = 1:[] in 2:a let a@(0:as) = 1:[] in 2:a in

Infos und Tipps zu den Prüfungen in Die Veranstaltungen des Moduls Algorithmen und Datenstrukturen werden gemeinsam in einer Modulabschluss-Prüfung geprüft. wissenschaftlicher Sinn einer Prüfung: Feststellen, ob ausreichende Grundlagen für weiterführende Veranstaltungen vorhanden sind. wissenschaftlich unsinnig, aber systemimmanent: Leistungsvergleich, Noten, peinliche Buchführung über den Ausbildungsgang zum Ausweis von Unterschieden Voraussetzung der Zulassung zur Prüfung sind 50% der Punkte aus den Übungen zu A&D+.

Mündliche Prüfung zum Modul A&D Welche Veranstaltungen werden geprüft? A&D Vorlesung in A&D + Übung in der UNIX-Umgebung Wer prüft? Prüfer Beisitzer Wie lange? 20 Minuten Wann? siehe Termine Was muss mitgebracht werden? Personalausweis bzw. Pass Studentenausweis in

Prüfungsziel in Test, inwieweit Veranstaltungsinhalte verstanden werden und angewendet werden können. Verschiedene Arten von Leistungen Reproduktion Transfer (konkret) Transfer (abstrakt) Prüfen auf eine möglichst gute Note hin.

Termine Robert 2. und 4. Februar-Woche, und ggf. später Alex Sczyrba 3. Februar-Woche, 3. März-Woche Terminvereinbarung bei Stefanie Lanfermann (M3-127) (Frau Lanfermann meldet die Prüfung im Dekanat an) weitere Prüfer: Prof. Dr. Stoye (mail an Heike Samuel) Prof. Dr. Hammer (14.,17.,18.2.) (mail an Gisela Weitekemper) Prof. Dr. Nattkemper (mail an Heike Samuel) Dr. Thies Pfeiffer (mail an Stefanie Lanfermann) in

in Materialien Vorlesungsfolien Übungsunterlagen Skript Prüfungsprotokolle, dabei jeweils beachten: Motivation Hintergrund Übungsgruppen Sprechstunden

Ablauf in Ausweiskontrolle Prüfer stellt die Fragen Beisitzer protkolliert hauptsächlich Einstiegsfrage nach 20 Minuten: Ende Student geht vor die Tür Prüfer und Beisitzer beraten sich kurz Student wird wieder hereingebeten Mitteilung der Note und Feedback

10 einfache Regeln zum Versemmeln einer Prüfung (G.Sauthoff) 1 erst 3 Tage vor dem Prüfungstermin mit der beginnen 2 alleine Lernen, obwohl das eigentlich noch nie gut geklappt hat 3 bei der das Skript ignorieren 4 Verzicht auf eine aktive Bearbeitung der Übungen 5 strikt vermeiden, ein -Programm selbst zu schreiben 6 Inhalte auswendig lernen 7 möglichst unausgeschlafen in die Prüfung kommen 8 auf die Frage nach Unklarheiten vor dem Beginn der Prüfung am besten sehr lang und breit erklären, wie schlecht vorbereitet man doch ist 9 kommunizieren, dass man die Begriffe in den Veranstaltungstiteln nicht erklären kann 10 trotz Krankheit zur Prüfung erscheinen in

Weitere Angebote in ZSB bietet Workshops an eher allgemeine Hinweise vieles ist wahrscheinlich schon bekannt eher sinnvoll bei extremer Prüfungsangst o.ä.

in In der Software-Entwicklung unterscheidet zwei Arten von : konkrete beziehen sich auf eine konkrete Repräsentation in der verwendeten Programmiersprache. Beispiele: Int, Char, Listen, Bäume; auch polymorphe sind konkret! abstrakte sind nicht an eine konkrete Repräsentation gebunden. Wie kann man dann überhaupt etwas definieren?

in Ein abstrakter Datentyp (ADT) ist, unabhängig von einer Programmiersprache, eine (nicht weiter spezifizierte) Wertemenge mit Operationen auf dieser Wertemenge, die bestimmte Axiome (Gleichungen) erfüllen die Menge der Operationen ist die Schnittstelle (API) nach außen konkrete Repräsentation der Daten in der Implementierung ist nicht nach außen sichtbar (Kapselung)

Warum? mit ADTs erhöht die Wiederverwendbarkeit von Programmen Beispiel: ADT Verzeichnis in Operationen auf Verzeichnissen: Erzeugen, Einfuegen, Loeschen, Sortieren, Bereinigen Programmierer, die Verzeichnisse verwenden, kennen NUR diese Operationen Verzeichnisse können konkret Listen, Bäume, Arrays,... sein, ggf. mit Zusatzoperationen Die konkrete Implementierung kann geändert werden, ohne dass andere Programme davon betroffen sind

spielen eine bedeutende Rolle in der Software-Entwicklung Alle modernen Programmiersprachen unterstützen ihre Verwendung Sie dienen der Spezifikation der nach außen sichtbaren Operationen und Eigenschaften Manchmal werden die Operationen mit Effizienzvorgaben verknüpft In werden abstrakte durch Module und durch Typklassen unterstützt. In Java/C++ geschieht dies durch Klassen ud Interfaces in

Mini-ADT Llist in Wir spezifizieren einen ADT Llist a analog zu Listen, dessen length-funktion die Komplexität O(1) haben soll. Operationen: empty : Llist a in O(1) (1) lcons : a Llista Llist a in O(1) (2) lhead : Llist a a in O(1) (3) ltail : Llist a Llist a in O(1) (4) llength : Llist a Int in O(1) (5) lapp : Llist a Llist a Llist a in O(n) (6)

Eigenschaften Eigenschaften der Operationen: lhead(lcons(x, xs)) = x (7) ltail(lcons(x, xs)) = xs (8) llength(lcons(x, empty)) = 1 (9) llength(lapp(xs, ys)) = llength(xs) + llength(ys)(10) lapp(x, empty) = x (11) und so weiter... in Können wir das spontan implementieren?

Module in Aus einigen Beispielen bekannt: Modulname Imports-Deklarationen Definitionen des Moduls 1 > module Meinmodul 2 > where 3 > import Data. List 4 in 5 > splits n = [ splitat k [1.. n] k <- [1..n -1] ]

Modulaufbau Was bisher verschwiegen wurde in Ein -Modul ist eine Datei, welche wie folgt eingeleitet wird: module <Name> (<Exportliste>) where Nur die und Funktionen, die in <Exportliste> angegeben werden, sind nach außen hin sichtbar Wenn <Exportliste> weggelassen wird, sind alle Definitionen nach außen sichtbar.

Exportieren von 1 module Liste ( List ) where 2 3 data List a = Nil 4 Cons a ( List a) Dies exportiert nur den Datentyp List, nicht aber die Konstruktoren Nil und Cons. Diese können in dem importierenden Modul nicht verwendet werden module Liste ( List ( Nil, Cons )) where exportiert auch die Konstruktoren alternativ: module Liste ( List (..)) where exportiert alle Konstruktoren eines Datentyps in

ADT-Beispiel: Holy Lists in Anhängen eines Elements am Ende einer Liste erfordert O(n) Schritte. Holy Lists erlauben diese Operation in O(1). Spezifikation: 1 > module Hlist (l2h,h2l, lcons, rcons ) where 2 > l2h :: [a] -> Hlist a -- in O(n) 3 > h2l :: Hlist a -> [a] -- in O(n) 4 > lcons :: a - > Hlist a - > Hlist a -- in O (1) 5 > rcons :: Hlist a - > a - > Hlist a -- in O (1) Holy Lists Stack Queue Set Multi-Set

Holy Lists: Eigenschaften in Einige der erwarteten Eigenschaften: 1 l2h. h2l = id ( Konvertierung ) 2 h2l. l2h = id ( " ) 3 4 reverse ( h2l ( rcons as a)) = a: h2l as Implementierung??? Zum Nachdenken über die Feiertage! Tipp: holy kommt von hole Listen mit Loch am Ende... Holy Lists Stack Queue Set Multi-Set

in Schöne Feiertage und Guten Rutsch Dieser Foliensatz wird noch weiter ergänzt im neuen Jahr. Holy Lists Stack Queue Set Multi-Set

ADT Holy Lists 1 type Hlist a in 2 empty :: Hlist a -- klassisch 3 cons :: a - > Hlist a - > Hlist a 4 append :: Hlist a - > Hlist a - > Hlist a 5 6 hcons :: Hlist a - > a - > Hlist a -- neu!! 7 8 l2h :: [a] -> Hlist a -- Konversion 9 h2l :: Hlist a -> [a] Holy Lists Stack Queue Set Multi-Set

Axiome für ADT Holy Lists in Forderungen an die Implementierung: Alle Eigenschaften von (:), [], (++) sollen auch für cons, empty, append gelten. h2l. l2h == id hcons h a == l2h (h2l h ++ [a]) Konvertierung (l2h l) und (h2l h) in O(n) hcons h a in O(1) Die letzte Anforderung ist die eigentliche Herausforderung! Holy Lists Stack Queue Set Multi-Set

Lösungsvorschlag Implementierung siehe Datei hlist.lhs Auszug: in 1 Hlist - Listen mit effizientem Anfuegen vorne und h 2 Append - Funktion ebenfalls in O (1) 3 4 Specification 5 6 > module Hlist (l2h,h2l, lcons, rcons ) where 7 > l2h :: [a] -> Hlist a -- in O(n) 8 > h2l :: Hlist a -> [a] -- in O(n) 9 > lcons :: a - > Hlist a - > Hlist a -- in O (1) uns so weiter... Holy Lists Stack Queue Set Multi-Set

ADT-Beispiel: Stack Ein Stack ( Stapel oder auch Keller ) ist ein Datentyp, der eine Menge von gleichartigen Elementen aufnehmen kann. Er unterstützt fünf Operationen: emptystack: Liefert einen Stack ohne Inhalt stackempty s: Fragt ob ein Stack s leer ist push x s: Legt ein neues Element x auf den Stack s pop s: Entfernt das oberste Element vom Stack top s: Liefert das oberste Element des Stacks s, ohne dieses zu entfernen Last-In-First-Out (LIFO)-Strategie: Das letzte Element, was auf den Stack gelegt wurde, ist das erste, was wieder heruntergenommen wird. in Holy Lists Stack Queue Set Multi-Set

Stack-Schnittstelle in 1 module Stack ( Stack,push,pop,top, 2 emptystack, stackempty ) where 3 4 emptystack :: Stack a 5 stackempty :: Stack a - > Bool 6 push :: a - > Stack a - > Stack a 7 pop :: Stack a - > Stack a 8 top :: Stack a -> a emptystack liefert einen neuen, leeren Stack stackempty überprüft, ob der Stack leer ist push legt ein Element auf den Stack pop entfernt das oberste Element vom Stack top liefert das oberste Element vom Stack in Holy Lists Stack Queue Set Multi-Set

Stack-Implementierung (1) in 1 > module Stack ( Stack, emptystack, stackempty, pop, push, top ) 2 > data Stack a = St [ a ] 3 4 > emptystack : : Stack a 5 > emptystack = St [ ] 6 7 > stackempty : : Stack a > Bool 8 > stackempty ( St [ ] ) = True 9 > stackempty ( St _) = F a l s e 10 11 > push : : a > Stack a > Stack a 12 > push x ( St xs ) = St ( x : xs ) Der Datentyp stack wird exportiert, aber nicht der Konstruktor St! NUR mittels Emptystack und push können Stacks erzeugt werden. Alternative: module Stack (Stack(St), emptystack... oder auch module Stack (Stack(..), emptystack... Holy Lists Stack Queue Set Multi-Set

Stack-Implementierung (2) in 14 > pop : : Stack a > Stack a 15 > pop ( St [ ] ) = e r r o r " pop : Stack empty! " 16 > pop ( St ( x : xs ) ) = St xs 17 18 > top : : Stack a > a 19 > top ( St [ ] ) = e r r o r " top : Stack empty! " 20 > top ( St ( x : xs ) ) = x Inspektion und Abbau von Stacks Holy Lists Stack Queue Set Multi-Set

Sicherheit... in Welche Rolle spielt der Konstruktor St? unterhalb von St stehen immer nur einfache Listen eigentlich kann man ihn ganz weglassen, oder? wie z.b. in... Holy Lists Stack Queue Set Multi-Set

Schlichte Stacks... in 1 > module Stack2 ( Stack, emptystack, stackempty, pop, push, top ) 2 > type Stack a = [ a ] 3 4 > emptystack : : Stack a 5 > emptystack = [ ] 6 7 > stackempty : : Stack a > Bool 8 > stackempty [ ] = True 9 > stackempty _ = F a l s e 10 11 > push : : a > Stack a > Stack a 12 > push x xs = x : xs 13 14 > pop : : Stack a > Stack a 15 > pop [ ] = e r r o r " pop : Stack empty! " 16 > pop ( x : xs ) = xs 17 18 > top : : Stack a > a 19 > top [ ] = e r r o r " top : Stack empty! " 20 > top ( x : xs ) = x Holy Lists Stack Queue Set Multi-Set

Der Unterschied... in Den Unterschied sieht man mit Hugs an Ausdrücken wie push y emptystack emptystack ++ "nonono" Auf den schlichten Stacks sind normale Listenoperationen ausführbar das will man gerade nicht, auch wenn es manchmal praktisch wäre, so wie im Falle von show. Holy Lists Stack Queue Set Multi-Set

Konvertierung in Die erste Lösung ist also die bessere. Ggf. brauchen wir Konvertierungsfunktionen zwischen Listen und Stacks. 1 > l2s :: [a] -> Stack a 2 > s2l : Stack a -> [a] Wo würde man die implementieren? Holy Lists Stack Queue Set Multi-Set

Konvertierung wo? in Im Modul Stack geht es einfach und effizient: 1 > l2s l = St l 2 > s2l (St l) = l außerhalb geht es auch recht einfach, aber weniger effizient: 1 > l2s = foldr push emptystack l 2 > s2l s = if s == emptystack then [] 3 > else top s : s2l pop s Das liegt daran, dass wir außen nichts über die interne Implementierung wissen. Holy Lists Stack Queue Set Multi-Set

Beispiel: Queue in Warteschlangen realisiert der ADT Queue. Eine Queue (Schlange) arbeitet (im Gegensatz zum Stack) nach dem FIFO (First In First Out)-Prinzip das erste Element, das einer Queue hinzugefügt wurde, ist auch das erste, das wieder entfernt wird Eine Queue stellt die Operationen enqueue, dequeue und front bereit, sowie emptyqueue und queueempty. Holy Lists Stack Queue Set Multi-Set

Queue-Schnittstelle in 1 module Queue ( Queue, emptyqueue, queueempty, 2 enqueue, dequeue, front ) where 3 4 emptyqueue :: Queue a 5 queueempty :: Queue a - > Bool 6 enqueue :: a - > Queue a - > Queue a 7 dequeue :: Queue a - > Queue a 8 front :: Queue a - > a emptyqueue liefert eine neue Queue queueempty überprüft, ob eine Queue leer ist enqueue fügt einer Queue ein neues Element hinzu dequeue entfernt das erste Element aus der Queue front liefert das erste Element der Queue in Holy Lists Stack Queue Set Multi-Set

Queue Implementierung (0) in Vorüberlegung: Warum kann es nicht effizient werden, Queues einfach als Listen zu implementieren? Also z.b. als data Queue a = Q [a]? Holy Lists Stack Queue Set Multi-Set

Queue Implementierung (1) in Moduldeklaration: 1 > module Queue ( Queue, emptyqueue, queueempty, 2 > enqueue, dequeue, f r o n t ) where 3 4 > emptyqueue : : Queue a 5 > queueempty : : Queue a > Bool 6 > enqueue : : a > Queue a > Queue a 7 > dequeue : : Queue a > Queue a 8 > f r o n t : : Queue a > a Auch hier wird kein Konstruktor exportiert! Holy Lists Stack Queue Set Multi-Set

Queue Implementierung (1) in Aufbau einer Queue: 10 > data Queue a = Q [ a ] [ a ] d e r i v i n g (Show, Eq) 11 12 > emptyqueue = Q [ ] [ ] 13 > queueempty (Q [ ] [ ] ) = True 14 > queueempty _ = F a l s e 15 16 > enqueue a (Q back f r o n t ) = Q ( a : back ) f r o n t Holy Lists Stack Queue Set Multi-Set

Queue Implementierung (3) 17 Abbau einer Queue: in 18 > dequeue (Q [ ] [ ] ) = e r r o r " dequeue : queue empty! " 19 > dequeue (Q back [ ] ) = dequeue (Q [ ] ( r e v e r s e back ) ) 20 > dequeue (Q back ( a : f r o n t ) ) = Q back f r o n t 21 22 > f r o n t (Q [ ] [ ] ) = e r r o r " f r o n t : queue empty! " 23 > f r o n t (Q back [ ] ) = f r o n t (Q [ ] ( r e v e r s e back ) ) 24 > f r o n t (Q back ( a : f r o n t ) ) = a Natürlich verwenden wir eine O(n)-Implementierung von reverse Holy Lists Stack Queue Set Multi-Set

Queue: Effizienz? emptyqueue liefert eine neue Queue queueempty überprüft, ob eine Queue leer ist enqueue fügt einer Queue ein neues Element hinzu dequeue entfernt das erste Element aus der Queue front liefert das erste Element der Queue Effizienzbetrachtung: Welchen Rechenaufwand haben die einzelnen Operationen? Was ist der Aufwand, wenn n Eintraege erzeugt, gelesen und entfernt werden? Amortisierte Effizienz von O(1) für dequeue. in Holy Lists Stack Queue Set Multi-Set

Varianten Was ändert sich, wenn man front und dequeue zusammenlegt als frondeq:: Queue a -> (a, Queue a)? 1 > frondeq ( Q [] []) = error " front : empty queue!" 2 > frondeq (Q back []) = frondeq (Q [] ( reverse back )) 3 > frondeq ( Q back ( a: front )) = (a, Q back front ) Jedes Element wird nur einmal gelesen und nur einmal revertiert. Nochmals die Sinnfrage: Warum kann man ohne abstrakte den Typ Queue nicht korrekt implementieren? in Holy Lists Stack Queue Set Multi-Set

Beispiel: Mengen in Mengen sind ein recht schwieriger Datentyp. Eine Menge ist eine ungeordnete Sammlung von unterschiedlichen Elementen Ein Element kann auf Mitgliedschaft in einer Menge hin überprüft werden, kann einer Menge hinzugefügt oder aus einer Menge entfernt werden Was ist daran schwierig? Holy Lists Stack Queue Set Multi-Set

Mengen-Schnittstelle in 1 module Set (Set, emptyset, setempty, inset, 2 addset, delset ) where 3 4 emptyset :: Set a 5 setempty :: Set a - > Bool 6 inset :: (Eq a) => a -> Set a -> Bool 7 addset :: (Eq a) => a -> Set a -> Set a 8 delset :: (Eq a) => a -> Set a -> Set a emptyset erzeugt eine leere Menge setempty überprüft, ob die Menge leer ist inset überprüft, ob ein Element in einer Menge enthalten ist addset fügt ein Element der Menge hinzu delset entfernt ein Element aus der Menge in Holy Lists Stack Queue Set Multi-Set

Mengen-Implementierung in Siehe Datei set.lhs Holy Lists Stack Queue Set Multi-Set

Multi-Set Synonyme: Multimenge Multi-Set Bag die Elementpositionen spielen keine Rolle wie bei Set Unterschied zu Listen Elemente können vielfach enthalten sein Unterschied zu Set wie bei Listen in Holy Lists Stack Queue Set Multi-Set

Multimenge die leere Multimenge, a die einelementige Multimenge, die genau ein Vorkommen von a enthält, x y die Vereinigung der Elemente von x und y; das + im Vereinigungszeichen deutet an, dass sich die Vorkommen in x und y akkumulieren. x = x = x y = x x y x (x y) z = x (y z) in Holy Lists Stack Queue Set Multi-Set

Multimenge in 1 bag :: [a] -> Bag a 2 bag [] = 3 bag (a:as) = a bag as Beobachtung Eine Liste x enthält alle Elemente von y, falls bag x = bag y. In diesem Fall heißt x Permutation von y. Holy Lists Stack Queue Set Multi-Set

r Datentyp Multimenge 1 module Bag ( Bag, emptybag, bagempty, inbag, 2 addbag, delbag, appendbag, 3 eqbag ) where 4 import List 5 6 emptybag :: Bag a 7 bagempty :: Bag a - > Bool 8 inbag :: Eq a = > a - > Bag a - > Bool 9 addbag :: Eq a = > a - > Bag a - > Bag a 10 delbag :: Eq a = > a - > Bag a - > Bag a 11 appendbag :: Bag a - > Bag a - > Bag a 12 eqbag :: Eq a = > Bag a - > Bag a - > Bool in Holy Lists Stack Queue Set Multi-Set

r Datentyp Multimenge emptybag erzeugt eine neue Multimenge bagempty überprüft, ob eine Multimenge leer ist inbag überprüft, ob ein Element in der Multimenge enthalten ist addbag fügt ein Element einer Multimenge hinzu delbag löscht ein Element aus einer Multimenge appendbag vereinigt zwei Multimengen Was soll man von diesen Operationen halten? headbag gibt das erste Element aus der Multimenge aus tailbag entfernt das erste Element aus der Multimenge Siehe Datei bag.lhs in Holy Lists Stack Queue Set Multi-Set

ADT-Fazit in ADTs sind wesentlich für die nachhaltige Software-Entwicklung. Einzelne Programmteile können unabhängig voneinander entwickelt verbessert und erweitert ausgetauscht werden und haben klare Schnittstellen (auch für das Verstehen des Programms) Holy Lists Stack Queue Set Multi-Set