Funktionale Programmierung mit Haskell



Ähnliche Dokumente
Funktionale Programmierung mit Haskell

Programmiersprachen und Übersetzer

4. Jeder Knoten hat höchstens zwei Kinder, ein linkes und ein rechtes.

Folge 19 - Bäume Binärbäume - Allgemeines. Grundlagen: Ulrich Helmich: Informatik 2 mit BlueJ - Ein Kurs für die Stufe 12

13. Binäre Suchbäume

Java Kurs für Anfänger Einheit 4 Klassen und Objekte

Lernziele: Ausgleichstechniken für binäre Bäume verstehen und einsetzen können.

1 topologisches Sortieren

Wiederholung ADT Menge Ziel: Verwaltung (Finden, Einfügen, Entfernen) einer Menge von Elementen

KONSTRUKTION VON ROT-SCHWARZ-BÄUMEN

Kapiteltests zum Leitprogramm Binäre Suchbäume

t r Lineare Codierung von Binärbbäumen (Wörter über dem Alphabet {, }) Beispiel code( ) = code(, t l, t r ) = code(t l ) code(t r )

Objektorientierte Programmierung

Einführung in die Programmierung

Grundlagen der Programmierung 2. Bäume

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Programmieren in Haskell Einführung

Theoretische Grundlagen der Informatik

Suchmaschinen. Universität Augsburg, Institut für Informatik SS 2014 Prof. Dr. W. Kießling 23. Mai 2014 Dr. M. Endres, F. Wenzel Lösungsblatt 6

Einfache Ausdrücke Datentypen Rekursive funktionale Sprache Franz Wotawa Institut für Softwaretechnologie

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

7 Rechnen mit Polynomen

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Handbuch. Artologik EZ-Equip. Plug-in für EZbooking version 3.2. Artisan Global Software

Informatik II Bäume. Beispiele. G. Zachmann Clausthal University, Germany Stammbaum. Stammbaum. Stammbaum

WS 2009/10. Diskrete Strukturen

Matrix42. Use Case - Sicherung und Rücksicherung persönlicher Einstellungen über Personal Backup. Version September

Scala kann auch faul sein

Anmerkungen zur Übergangsprüfung

Vorkurs C++ Programmierung

Algorithmen und Datenstrukturen

Würfelt man dabei je genau 10 - mal eine 1, 2, 3, 4, 5 und 6, so beträgt die Anzahl. der verschiedenen Reihenfolgen, in denen man dies tun kann, 60!.

368 4 Algorithmen und Datenstrukturen

1. Formale Sprachen 1.2 Grammatiken formaler Sprachen

Algorithmik II. a) Fügen Sie in einen anfangs leeren binären Baum die Schlüsselfolge 20, 28, 35, 31, 9, 4, 13, 17, 37, 25 ein.

Software Engineering Klassendiagramme Assoziationen

1 Mathematische Grundlagen

Idee: Wenn wir beim Kopfknoten zwei Referenzen verfolgen können, sind die Teillisten kürzer. kopf Eine Datenstruktur mit Schlüsselwerten 1 bis 10

2.11 Kontextfreie Grammatiken und Parsebäume

ALP I. Funktionale Programmierung

Binäre Bäume Darstellung und Traversierung

Primzahlen und RSA-Verschlüsselung

3.2 Binäre Suche. Usr/local/www/ifi/fk/menschen/schmid/folien/infovk.ppt 1

Wir machen neue Politik für Baden-Württemberg

Suchbäume. Annabelle Klarl. Einführung in die Informatik Programmierung und Softwareentwicklung

Benutzerhandbuch - Elterliche Kontrolle

Binärbäume als weiteres Beispiel für abstrakte Datentypen in PVS mit in Knoten gespeicherten Werten vom Typ T:

Erwin Grüner

Typdeklarationen. Es gibt in Haskell bereits primitive Typen:

Bedienungsanleitung: Onlineverifizierung von qualifiziert signierten PDF-Dateien

Grundbegriffe der Informatik

Einführung in die Programmierung

Algorithmen und Datenstrukturen Suchbaum

Lehrer: Einschreibemethoden

XML-Austauschformat für Sicherheitsdatenblätter

Kapitel MK:IV. IV. Modellieren mit Constraints

Objektorientierte Programmierung. Kapitel 12: Interfaces

Diana Lange. Generative Gestaltung Operatoren

Erstellen einer Collage. Zuerst ein leeres Dokument erzeugen, auf dem alle anderen Bilder zusammengefügt werden sollen (über [Datei] > [Neu])

13 OOP MIT DELPHI. Records und Klassen Ein Vergleich

WPF Steuerelemente Listbox, ComboBox, ListView,

EndTermTest PROGALGO WS1516 A

Informationsblatt Induktionsbeweis

Dokumentenverwaltung im Internet

Das Typsystem von Scala. L. Piepmeyer: Funktionale Programmierung - Das Typsystem von Scala

Institut für Programmierung und Reaktive Systeme 25. August Programmier-Labor Übungsblatt. int binarysearch(int[] a, int x),

Bitte wenden. Name: KURSARBEIT NR. 4 (10 DIFF GA) Seite 1

Typumwandlungen bei Referenztypen

Funktionale Programmierung mit Haskell

Grundlagen der Künstlichen Intelligenz

Systeme 1. Kapitel 6. Nebenläufigkeit und wechselseitiger Ausschluss

Modellierung und Programmierung 1

IT-Basics 2. DI Gerhard Fließ

Grundlagen der höheren Mathematik Einige Hinweise zum Lösen von Gleichungen

Objects First With Java A Practical Introduction Using BlueJ. Mehr über Vererbung. Exploring polymorphism 1.0

Vererbung & Schnittstellen in C#

Simon die linke Hand des Handwerks

Motivation. Formale Grundlagen der Informatik 1 Kapitel 5 Kontextfreie Sprachen. Informales Beispiel. Informales Beispiel.

Programmierung 2. Übersetzer: Code-Erzeugung. Sebastian Hack. Klaas Boesche. Sommersemester

Energetische Klassen von Gebäuden

Grundlagen von Python

Hochschule Ravensburg-Weingarten. Technik Wirtschaft Sozialwesen. Projektarbeit

Datenstrukturen & Algorithmen

Algorithms & Datastructures Midterm Test 1

1. Adressen für den Serienversand (Briefe Katalogdruck Werbung/Anfrage ) auswählen. Die Auswahl kann gespeichert werden.

Informatik GK 12 Klassen Klassen programmieren in Delphi am Beispiel der konkreten Klasse Auto

Beweisbar sichere Verschlüsselung

Evident VDDS-Anbindung von MIZ

15 Optimales Kodieren

AGROPLUS Buchhaltung. Daten-Server und Sicherheitskopie. Version vom b

Menü auf zwei Module verteilt (Joomla 3.4.0)

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

Übungen zu Einführung in die Informatik: Programmierung und Software-Entwicklung: Lösungsvorschlag

Mathematischer Vorbereitungskurs für Ökonomen

Die Gleichung A x = a hat für A 0 die eindeutig bestimmte Lösung. Für A=0 und a 0 existiert keine Lösung.

Skript und Aufgabensammlung Terme und Gleichungen Mathefritz Verlag Jörg Christmann Nur zum Privaten Gebrauch! Alle Rechte vorbehalten!

Informatik 11 Kapitel 2 - Rekursive Datenstrukturen

Objektorientierte Programmierung

Transkript:

Funktionale Programmierung mit Haskell Prof. Dr. Hans J. Schneider Lehrstuhl für Programmiersprachen und Programmiermethodik Friedrich-Alexander-Universität Erlangen-Nürnberg Sommersemester 2011 I. Die Sprache Haskell 1. Einführung in Haskell 2. Funktionen als Basis der Programmierung 3. Algebraische Typen 4. Grundlagen des Typsystems 5. Strukturierung durch Typklassen 6. Verbergen von Information 7. Sprachergänzungen II. Fallstudien c Hans J. Schneider 2011

Beispiel einer Typklassenhierarchie: Bäume Hierarchie: Heap BinT ree T ree DerivT ree BinSearchT ree Balanced Ein Baum hat eine Wurzel, die keinen Vorgänger hat und weitere Knoten, die jeweils genau einen Vorgänger haben. Knoten ohne Nachfolger heißen Blätter. Bei einem binären Baum hat jeder Knoten höchstens zwei Nachfolger. (Strengere Definition: genau zwei oder keinen Nachfolger) Bei einem Ableitungsbaum sind die Blätter mit terminalen Symbolen, die anderen Knoten mit nichtterminalen markiert. Funktionale Programmierung mit Haskell 5.1

Überblick Wir betrachten zunächst die (Teil-)Hierarchie der binären Bäume, ohne jedoch Heap und Balanced genauer zu implementieren. Wie ist die allgemeine Syntax der Typklassendefinition? Beispiele für Default-Methoden Syntax der Instanzendeklarationen Standardimplementierung der binären Bäume Einordnung in die Baum-Hierarchie und in Show Mehrfachvererbung Ableitungsbäume Funktionale Programmierung mit Haskell 5.2

Bäume in Haskell class Tree h where isemptytree :: h a -> Bool root :: h a -> a height, weight :: h a -> Integer successors :: h a -> [h a] preorder :: h a -> [a] showtree :: (Show a) => h a -> String Binäre Bäume class (Tree h) => BinTree h where consleaf :: a -> h a constree :: (a, h a, h a) -> h a left, right :: h a -> h a inorder :: h a -> [a] Ableitungsbäume class (Tree h) => DerivTree h where termnode :: a -> h a nontermnode :: (Eq a) => (a, [a]) -> [h a] -> h a Funktionale Programmierung mit Haskell 5.3

Spezielle binäre Bäume Hierarchie: Heap BinT ree BinSearchT ree Balanced Ein Heap ist ein binärer Baum, in dem für jeden Teilbaum gilt, dass das Element an der Wurzel kleiner ist als beide Nachfolger (oder es ist stets größer als die Nachfolger). Eigentlich muss man sich auf fast vollständige binäre Bäume beschränken. In einem binären Suchbaum gilt für jeden Teilbaum, dass das Element an der Wurzel größer ist als alle Elemente im linken Unterbaum und kleiner als alle im rechten Unterbaum. Ein binärer Suchbaum heißt k-balanciert, wenn für jeden Knoten die Höhendifferenz seiner Unterbäume k ist. Interessant nur: k = 1 Funktionale Programmierung mit Haskell 5.4

Binäre Bäume in Haskell Binäre Suchbäume: class (BinTree h) => BinSearchTree h where insertordered :: Ord a => a -> h a -> h a isin :: Ord a => h a -> a -> Bool removeordered :: Ord a => a -> h a -> h a Balancierte Bäume: class (BinSearchTree h) => Balanced h where insertbalanced :: Ord a => h a -> a -> h a balance :: h a -> Integer rebalancesubtree :: Ord a => h a -> h a Heap-Bäume: class (BinTree h) => Heap h where insertheap :: Ord a => a -> h a -> h a removefromheap :: Ord a => h a -> h a (Beim Einfügen wählen wir den Unterbaum mit weniger Elementen.) Die Haskell-Syntax lässt nicht zu, dass Ord a in der Voraussetzung der Klassendefinition erscheint. Funktionale Programmierung mit Haskell 5.5

Allgemeine Form einer Typklassendeklaration A class declaration introduces a new class and the operations (class methods) on it. class (Tree h) => BinTree h where consleaf :: a -> h a constree :: (a, h a, h a) -> h a left, right :: h a -> h a inorder :: h a -> [a] A class declaration has the general form (Report No. 4.3.1): class cx => C u where cdecls This introduces a new class name C. The type variable u is scoped only over the class method signatures in the class body. The context cx specifies the superclasses of C. The only type variable that may be referred to in cx is u. The superclass relation must not be cyclic. Funktionale Programmierung mit Haskell 5.6

Methoden in einer Typklassendeklaration The cdecls part of a class declaration contains three kinds of declarations. The class declaration introduces new class methods vi vi :: cxi => ti... Beispiel: class (Tree h) => DerivTree h where termnode :: a -> h a nontermnode :: (Eq a) => (a, [a]) -> [h a] -> h a Class methods share the top level namespace with variable bindings [and field names]; they must not conflict with other top level bindings in scope. The ti must mention u. It may mention type variables w other than u, in which case the type of vi is polymorphic in both u and w. The cxi may constrain only w. Funktionale Programmierung mit Haskell 5.7

Optionale Angaben in der Typklassendefinition The cdecls may contain a fixity declaration for any of the class methods. (A fixity declaration gives the fixity and binding precedence of one or more operators.) The cdecls may contain a default class method for any of the vi. Beispiel: showstack st = if isemptystack st then "!" else (show (top st)) ++ " - " ++ (showstack (pop st)) The default class method for vi is used if no binding for it is given in a particular instance declaration. The default method declaration is a normal value definition, except that the left hand side may only be a variable or function definition. Bemerkung: Dies schließt an dieser Stelle die Definition durch Mustervergleich aus. Funktionale Programmierung mit Haskell 5.8

Default-Methoden für Bäume Schnittstelle für alle Bäume: class Tree h where isemptytree :: h a -> Bool root :: h a -> a height :: h a -> Integer -- Default: Uebung! weight :: h a -> Integer -- Default: s.u. successors :: h a -> [h a] preorder :: h a -> [a] -- Default: Uebung! showtree :: (Show a) => h a -> String -- Default: spaeter Default-Implementierung für weight: weight t = if isemptytree t then 0 -- else if length (successors t) == 0 then 1 else 1 + sum (map weight (successors t)) Es ist möglich, in der Default-Implementierung Funktionen zu verwenden, die selbst noch nicht implementiert sind! Default-Implementierungen gelten für alle Implementierungen von Bäumen und deren Unterklassen. Funktionale Programmierung mit Haskell 5.9

Default-Methoden für binäre Bäume class (Tree h) => BinTree h where consleaf :: a -> h a constree :: (a, h a, h a) -> h a left, right :: h a -> h a inorder :: h a -> [a] Auch die für die Oberklasse definierten Methoden dürfen in Default- Implementierungen verwendet werden: inorder t = if isemptytree t then [] else inorder (left t) ++ [root t] ++ inorder (right t) left t = (successors t)!!0 -- Fehlermeldung fehlt! right t = (successors t)!!1 Es ist nicht möglich, nachträglich Default-Implementierungen für Methoden der Oberklasse anzugeben, aber es ist möglich, diese für konkrete Instanzen durch effizientere Implementierungen zu ersetzen, z.b. left und right. Funktionale Programmierung mit Haskell 5.10

Instanzendeklarationen Eine Instanzendeklaration legt fest, dass ein (an anderer Stelle definierter) Typ Instanz einer Typklasse sein soll. (Report No. 4.3.2) Beispiel: instance (Show a) => Show (T_Stack a) where show = showstack An instance declaration introduces an instance of a class. Let class cx => C u where { cbody } be a class declaration. The general form of the corresponding instance declaration is: instance cx => C (T u1... uk) where { d } where k 0. The type (T u1... uk) must take the form of a type constructor T applied to simple type variables u1,... uk; furthermore, [T must not be a type synonym, and] the ui must all be distinct. Funktionale Programmierung mit Haskell 5.11

Bemerkungen zur Instanzendeklaration The declarations d may contain bindings only for the class methods of C. Zusätzliche Funktionsdeklarationen für diesen Typ können außerhalb der Instanzendeklaration vorgenommen werden (beispielsweise bei der Typdeklaration). Sie gelten dann nur für diese Instanz, nicht für die anderen Typen der Klasse. instance Stack [] where... erlaubt den Zugriff auf alle Elemente des Stacks mit!!. Wird ein Typ zur Instanz einer Klasse gemacht, die eine Oberklasse voraussetzt, so muss er auch zur Instanz der Oberklasse gemacht werden. As in the case of default class methods, the method declarations must take the form of a variable or function definition. Aber Mustervergleich geht hier, sofern es sich um algebraische Datentypen handelt. The declarations may not contain any type signatures or fixity declarations, since these have already been given in the class declaration. Funktionale Programmierung mit Haskell 5.12

Die Standardimplementierung der binären Bäume Festlegung einer Implementierung als Termalgebra: data StandBinTree a = EmptyBinTree Node(a, StandBinTree a, StandBinTree a) Einbettung in die Klasse BinTree: (Es müssen mindestens die Funktionen implementiert werden, für die keine Default-Implementierung vorliegt.) instance BinTree StandBinTree where consleaf e = Node(e, EmptyBinTree, EmptyBinTree) constree = Node Ersetzen der ineffizienten Default-Implementierungen: left(node(_, l, _)) = l right(node(_, _, r)) = r Funktionale Programmierung mit Haskell 5.13

Einordnung in die Klasse Tree Man muss die Implementierung in alle gewünschten Klassen explizit einordnen. Die Einordnung in BinTree setzt voraus, dass die Implementierung auch in Tree eingeordnet wird: class (Tree h) => BinTree h where... Einordnung in die Klasse Tree: instance Tree StandBinTree where isemptytree(emptybintree) = True isemptytree(node(_,_,_)) = False root(emptybintree) = error "root of empty tree" root(node(r, _, _)) = r successors(emptybintree) = [] successors(node(_, l, r)) = [l, r] Hier darf Mustervergleich verwendet werden, da ein algebraischer Datentyp vorliegt. Funktionale Programmierung mit Haskell 5.14

Mehrfachvererbung Mehrfachvererbung ist auf zwei Wegen möglich: Eine Klasse kann mehrere Oberklassen besitzen. class (Eq a, Show a) => C a where... Ein Typ kann zu Instanzen von mehreren Klassen gemacht werden: instance BinTree StandBinTree where... instance (Show a) => Show (StandBinTree a) where... Die Instanzenbildung erfolgt auch längs des Vererbungsweges schrittweise: instance Tree StandBinTree where... instance BinTree StandBinTree where... instance BinSearchTree StandBinTree where... instance Balanced StandBinTree where... Die Reihenfolge spielt keine Rolle. Funktionale Programmierung mit Haskell 5.15

Bemerkungen zur Mehrfachvererbung Doppelte Verwendung eines Methodenbezeichners längs unterschiedlicher Wege bei Mehrfachvererbung ist nur über qualifizierten Import möglich und dann eindeutig. Eine spezielle Anwendung (Report 4.3.1): A class declaration with no where part may be useful for combining a collection of classes into a larger one that inherits all of the class methods in the original ones. Beispiel: class (Read a, Show a) => Textual a If a type is an instance of all superclasses, it is not automatically an instance of the subclass, even though the subclass has no immediate class methods. The instance declaration must be given explicitly with no where part. Wenn man alle Instanzendeklarationen zu einem Typ an einer Stelle des Programms sammelt, hat man einen guten Überblick über das, was definiert ist. (Das ist aber nicht in jedem Fall praktisch.) Funktionale Programmierung mit Haskell 5.16

Ausgabe von Bäumen Default-Implementierung für beliebige Bäume: showtree t = if isemptytree t then "-" else if length(successors t) == 0 then "(" ++ show (root t) ++ ")" else "(" ++ show (root t) ++ " " ++ sh (successors t) ++ ")" where sh [] = "" sh (hd:[]) = showtree hd sh (hd:tl) = showtree hd ++ " " ++ sh tl Einordnung der Standardimplementierung binärer Bäume in Show: instance (Show a) => Show (StandBinTree a) where show = showtree Bemerkung: sh entspricht beinahe map showtree. Funktionale Programmierung mit Haskell 5.17

Beispiel-Ausgaben mit der Default-Implementierung Beispiel: t2 = Node(87, Node(123, Node(471, EmptyBinTree, EmptyBinTree), Node(200, EmptyBinTree, EmptyBinTree) ), Node(256, EmptyBinTree, Node(317, EmptyBinTree, EmptyBinTree)) ) Ausgabe: Main> t2 (87 (123 (471 - -) (200 - -)) (256 - (317 - -))) Die Default-Lösung druckt die Blätter entsprechend der Definition als Wurzel mit leeren Unterbäumen. Kann man diese leeren Unterbäume weglassen? Funktionale Programmierung mit Haskell 5.18

Alternative Ausgabe für binäre Bäume Neue Definition der Ausgabe nur für binäre Bäume: instance (Show a) => Show (StandBinTree a) where show(emptybintree) = "()" show(node(root, EmptyBinTree, EmptyBinTree)) = "(" ++ show root ++ ")" show(node(root, l, r)) = "(" ++ show root ++ " " ++ show(l) ++ " " ++ show(r) ++ ")" Beispielausgabe: Main> t2 (87 (123 (471) (200)) (256 () (317))) Man kann aber auch die Implementierung aus der Oberklasse benutzen: Main> showtree t2 "(87 (123 (471 - -) (200 - -)) (256 - (317 - -)))" Funktionale Programmierung mit Haskell 5.19

Ableitungsbäume Ableitungsbaum = Graphische Darstellung einer Ableitung bei kontextfreien Grammatiken expr expr + term term term * factor expr ::= expr + term expr - term term term ::= term * factor term / factor factor factor var factor var num factor ::= var num ( expr ) Terminale Symbole: + - * / var num ( ) Nichtterminale Symbole: expr term factor Startsymbol: expr Funktionale Programmierung mit Haskell 5.20

Ableitungsbäume in Haskell Wiederholung: Klasse der Bäume class Tree h where isemptytree :: h a -> Bool root :: h a -> a height, weight :: h a -> Integer successors :: h a -> [h a] preorder :: h a -> [a] showtree :: (Show a) => h a -> String Bei einem Ableitungsbaum sind die Blätter mit terminalen Symbolen, die anderen Knoten mit nichtterminalen markiert. Klasse der Ableitungsbäume: class (Tree h) => DerivTree h where termnode :: a -> h a nontermnode :: (Eq a) => (a, [a]) -> [h a] -> h a (a, [a]) ist der Typ der Produktion. Für a wird stets String eingesetzt. Funktionale Programmierung mit Haskell 5.21

Implementierung der Ableitungsbäume Ein beliebiger Baum ist entweder leer, oder er besteht aus einem Knoten (mit Inhalt) und einer Liste von Unterbäumen: data StandArbTree a = EmptyArbTree ArbNode(a, [StandArbTree a]) Wir machen diese Implementierung zu einer Instanz der Klasse der Ableitungsbäume: instance DerivTree StandArbTree where Ein Blatt ist mit einem terminalen Symbol s markiert: termnode s = ArbNode(s, []) Ein nichtterminal markierter Knoten entsteht dadurch, dass eine Folge von Unterbäumen gemäß einer Produktion zusammengefasst wird: nontermnode (lhs, rhs) subtrees = if map root subtrees == rhs then ArbNode(lhs, subtrees) else error "production not applicable" Die Wurzeln der Unterbäume müssen mit den Symbolen der rechten Seite der Produktion übereinstimmen! Funktionale Programmierung mit Haskell 5.22

Allgemeine Bäume sind Bäume Einordnung in die Klasse Tree: instance Tree StandArbTree where isemptytree EmptyArbTree = True isemptytree (ArbNode(_, _)) = False root EmptyArbTree = error "root of empty tree" root(arbnode(r, _)) = r successors EmptyArbTree = [] successors(arbnode(_, s)) = s Einordnung in die Klasse Show: instance (Show a) => Show (StandArbTree a) where show = showtree Man kann natürlich eine optisch schönere Ausgabe implementieren. Funktionale Programmierung mit Haskell 5.23

Einfaches Beispiel Beispiele von Blättern: d1, d2, d3 :: StandArbTree String d1 = termnode "expr" d2 = termnode "+" d3 = termnode "term" Da termnode eine Klassenmethode ist, muss die gewünschte Implementierung explizit angegeben werden. (Eine genügt hier aber!) Main> d1 ("expr") Die Produktion expr ::= expr + term: p1 = ("expr", ["expr", "+", "term"]) Aufbau eines kleinen Ableitungsbaumes: d4 = nontermnode p1 [d1, d2, d3] d5 = nontermnode p1 [d4, d2, d3] Main> d5 ("expr" ("expr" ("expr") ("+") ("term")) ("+") ("term")) Funktionale Programmierung mit Haskell 5.24