Paradigmen zur Algorithmenbeschreibung In einem objektorientierten Algorithmus werden Datenstrukturen und Methoden zu einer Klasse zusammengefasst. Von jeder Klasse können Objekte gemäß der Datenstruktur erstellt und über die Methoden manipuliert werden. In einem imperativen Algorithmus gibt es Variable, die verschiedene Werte annehmen können. Die Menge aller Variablen und ihrer Werte sowie der Programmzähler beschreiben den Zustand zu einem bestimmten Zeitpunkt. Ein Algorithmus bewirkt eine Zustandstransformation. Ein Algorithmus heißt funktional, wenn die zugrunde liegende Berechnungsvorschrift mittels einer Sammlung von Funktionen definiert wird. Die Funktionsdefinitionen dürfen insbesondere Rekursionen und Funktionen höherer Ordnung enthalten. Ein logischer (deduktiver) Algorithmus führt Berechnungen durch, indem er aus Fakten und Regeln durch Ableitungen in einem logischem Kalkül Ziele beweist. Rückblick und Ausblick A-1 Algorithmus von Euklid Der folgende, in einer imperativen Programmiersprache formulierte Algorithmus von Euklid berechnet den größten gemeinsamen Teiler der Zahlen x , y ∈ N mit x ≥ 0 und y > 0: a := x; b := y; while b do r := a := b := od Rückblick und Ausblick # 0 a mod b; b; r A-2 Algorithmus von Euklid Variable r a b z2 – 36 52 z5 36 52 36 z8 16 36 16 z11 4 16 4 z14 0 4 0 ggT(36, 52) = 4 Durchlaufene Zustände: z0, z1, z2, ... , z14 Zustandstransformation: z0 7−→ z14 Rückblick und Ausblick A-3 Imperatives Programmieren • In einem imperativen Programm gibt es Variable, die Werte speichern können. Die Variablen und ihre Werte legen den Zustand des Programms fest. Der Zustand ändert sich mit dem Ablauf des Programms. • Die Wertzuweisung gestattet es, den Zustand zu verändern. • ◦ Mit einer Sequenz können Anweisungen nacheinander ausgeführt werden. ◦ Die Selektion erlaubt die Auswahl zwischen Anweisungen. ◦ Anweisungen können mit der Iteration wiederholt werden. • ◦ Eingabe-Anweisungen ermöglichen es, den Zustand von außen zu beeinflussen. ◦ Ausgabe-Anweisungen erstellen einen Ausdruck des Zustands (oder eines Teils davon). Rückblick und Ausblick A-4 Imperatives Programmieren • Prozeduren: Abstraktionen von Anweisungen • Funktionen: Abstraktionen von Ausdrücken • Datentypen: ◦ Primitive Datentypen: boolean, char, int, real ◦ Zusammengesetzte Datentypen: enumeration, array, record, pointer ◦ Typdeklarationen: Abstraktionen von Datentypen Typsysteme können unabhängig von Paradigmen und Sprachen definiert und untersucht werden. Rückblick und Ausblick A-5 Imperatives Programmieren • Weitere Kontrollstrukturen • Module • Ausnahmebehandlung • Parallelverarbeitung Rückblick und Ausblick A-6 Algorithmus von Euklid • imperative/iterative Formulierung: while b # do r := a := b := od 0 a mod b; b; r • funktionale/rekursive Formulierung: ( a b=0 ggT(a,b) = ggT(b, a mod b) b = 6 0 Rückblick und Ausblick A-7 Algorithmus von Euklid (Scheme, funktional) (define (ggT a b) (if (= b 0) a (ggT b (remainder a b)))) (ggt 36 52) > 4 Rückblick und Ausblick A-8 Funktionales Programmieren • Ein funktionaler Algorithmus formuliert die Berechnungsvorschrift durch eine Menge von Funktionen. • Die Funktionen können wechselseitig-rekursiv oder auch • von höherer Ordnung (Funktionale) sein. Rückblick und Ausblick A-9 Funktionen höherer Ordnung (define (inc n) (+ n 1)) (define (square x) (* x x)) (define (cube x) (* x x x)) (define (sum f a next b) (if (> a b) 0 (+ (f a) (sum f (next a) next b)))) Rückblick und Ausblick A-10 (define (sum-square a b) (sum square a inc b)) (define (sum-cube a b) (sum cube a inc b)) > (sum-square 1 4) 30 > (sum-cube 1 4) 100 Rückblick und Ausblick A-11 Deduktive Algorithmen • Ein deduktiver (logischer) Algorithmus führt Berechnungen durch, indem er aus Fakten und Regeln weitere Fakten, sogenannte Anfragen (Ziele), beweist. • Fakten: P • Regeln: P if Q1 and Q2 and ... and Qk Regeln dieser Form werden auch Horn-Klauseln genannt. Fakten können als Regeln mit k = 0 gesehen werden. • Ein logisches Programm ist eine endliche Menge von Fakten und Regeln. Die folgenden Beispiele wurden in der deduktiven Programmiersprache Prolog geschrieben. Rückblick und Ausblick A-12 Beispiel: Eingabe der Fakten und Regeln vater(johann,heinrich). vater(johann,thomas). vater(heinrich,carla). vater(thomas,erika). vater(thomas,klaus). vater(thomas,golo). vater(thomas,monika). vater(thomas,elisabeth). vater(thomas,michael). verheiratet(johann,julia). verheiratet(heinrich,maria). verheiratet(thomas,katia). mutter(X,Y) :- vater(Z,Y), verheiratet(Z,X). geschwister(X,Y) :- vater(Z,X), vater(Z,Y), X = / = Y. Rückblick und Ausblick A-13 Beispiel: Eingabe der Anfragen Nachdem das Programm eingelesen wurde, können Anfragen gestellt werden, die von einer Interferenzmaschine, zum Beispiel einem Prolog-Interpreter, beantwortet werden. Anfragen werden nach der Eingabeaufforderung | ?- gestellt: | ?- geschwister(thomas, heinrich). true ? ; no | ?- geschwister(thomas, golo). no Die Ausgabe true bedeutet, dass die Anfrage positiv beantwortet wurde, das heißt, dass Thomas und Heinrich Geschwister sind. Nach einem Fragezeichen erwartet der Interpreter Anweisungen, wie fortzufahren ist. Ein Semikolon ist die Aufforderung, nach weiteren Lösungen zu suchen. Rückblick und Ausblick A-14 Beispiel: Eingabe der Anfragen Falls eine Anfrage eine Variable enthält, werden alle Belegungen für die Variablen ermittelt, die die Aussage wahr werden lassen. Wenn wir die Geschwister von Golo suchen, stellen wir die folgende Anfrage. | ?- geschwister(X,golo). X = erika ? ; X = klaus ? ; X = monika ? ; X = elisabeth ? ; X = michael ? ; no Erika, Klaus, Monika, Elisabeth und Michael sind also die Geschwister von Golo. Rückblick und Ausblick A-15 Beispiel: Eingabe der Anfragen Eine Anfrage kann mehr als eine Variable enthalten. Durch | ?- geschwister(X,Y). werden insgesamt 32 Geschwisterpaare ermittelt, da Paare wegen der Symmetrie der Relation doppelt ausgegeben werden. Rückblick und Ausblick A-16 Algorithmus von Euklid (Prolog, deduktiv) ggt(A,0,A). ggt(A,B,Z) :- U is A mod B, ggt(B,U,Z). | ?- ggt(36,52,Z). Z = 4 ? Rückblick und Ausblick A-17 Funktionen und Relationen • Eine Funktion f : X −→ Y ordnet jedem Element der Menge X (Definitionsbereich) genau ein Element der Menge Y (Wertebereich) zu: x 7−→ f (x ) • Eine Relation R ⊆ X × Y besteht aus Paaren (x , y ) ∈ X × Y . Dabei kann es zu einem x ∈ X mehrere Elemente y ∈ Y mit (x , y ) ∈ R geben. Rückblick und Ausblick A-18 Deduktives Programmieren • Deduktives Programmieren beschäftigt sich mit Relationen. Funktionen sind ja spezielle Relationen. • Fakten und Regeln können als Eingabe von Relationen bzw. als Konstruktionsvorschriften für Relationen angesehen werden. • Anfragen überprüfen, ob Paare in einer bestimmten Relation stehen oder ermitteln solche Paare: ◦ Eine Anfrage der Form P(x , y ) überprüft, ob x und y bezüglich P in Relation stehen. ◦ Die Anfrage P(X , y ) berechnet alle x mit (x , y ) ∈ P. ◦ Anfragen der Form P(X , Y ) führen zur Ausgabe aller Paare (x , y ) mit (x , y ) ∈ P. Rückblick und Ausblick A-19 Deklaratives Programmieren • In deklarativen Programmiersprachen wird aus einer nach bestimmten Regeln gebildeten mathematischen Formulierung eines Problems automatisch ein Programm erzeugt. • Die formale Problembeschreibung kann z. B. auf der Prädikatenlogik (logische Programmiersprachen) oder dem λ-Kalkül (funktionale Programmiersprachen) basieren. • Deklarative Programmiersprachen haben sich vom Konzept der ausführenden Maschine gelöst. Rückblick und Ausblick A-20 Entwicklung der Programmiersprachen . . 2000 C# 1995 Programmiersprachen in der Informatikausbildung JAVA SCHEME (standard) 1990 • Algol OCCAM C++ 1985 CSP ADA 1980 SMALLTALK80 MODULA−2 1975 C SCHEME PROLOG PASCAL 1970 ALGOL68 • Algol68 • Modula-2 LOGO SIMULA • Scheme 1965 PL/I BASIC 1960 1955 COBOL ALGOL FORTRAN LISP • Java . Rückblick und Ausblick A-21 Programmiersprachen • Imperative Programmiersprachen: Cobol, Fortran, PL/I, Basic, Algol, Algol68, Pascal, Modula-2, C, Ada • Funktionale Programmiersprachen: Lisp, Scheme, ML, Haskell, Scala. Die Sprache Scala ist eine Erweiterung von Java. • Objektorientierte Programmiersprachen: C++, Eiffel, Smalltalk, Java, C#, Oberon • Logische Programmiersprachen: Prolog • Skriptsprachen, Spezialsprachen Rückblick und Ausblick A-22 Bisherige Entwicklung von Java 1992–1995 Jan. Anfang Dez. Jan. Mai Februar Ende Dezember Juli März 1996 1997 1998 1999 2000 2002 2004 2006 2011 2014 Java-Vorläufer, zuerst unter dem Namen „Oak“. Neu: Applets (little applications) JDK 1.0 (Java Development Kit) JDK 1.1 JDK 1.2, wurde umbenannt in „Java 2 Plattform“ Java 2, JDK 1.3 Java 2, JDK 1.4 Java 2, JDK 5.0 (interne Versionsnummer: 1.5.0) „Tiger“ Java Standard Edition 6 („Mustang“) Java Standard Edition 7 („Dolphin“) Java Standard Edition 8 Eigentlich sollte Java 8 im September 2013 veröffentlicht werden. Rückblick und Ausblick A-23 Fragestellung: Statischer/dynamischer Scope Wie lautet die Ausgabe des folgenden Programms? public class Test { static int x=7; static void p() {x=0;} // x ist eine globale Variable. public static void main(String[] args) { int x=5; p(); System.out.println(x); } } • Statischer Scope: Ausgabe: 5, Dynamischer Scope: Ausgabe: 0. • Die Semantik der Methode p hängt also davon ab, wie die globale Variable x genommen wird. • Wir wissen: Java gibt die 5 aus. Rückblick und Ausblick A-24 Java 8 • Die wichtigste Erweiterung ist das Projekt Lambda. Das Ziel dieses Projektes ist die Einführung von Closures. Damit wird die Bedeutung von globalen Variablen definiert. Globale Variable können vielfältiger als das obige Beispiel sein. • Funktionale Schnittstellen sind Schnittstellen, die genau eine abstrakte Methode enthalten. Abstrakte Klassen mit einer abstrakten Methode zählt man nicht zu den funktionalen Schnittstellen. Schnittstellen sollen auch statische Methoden enthalten können. • Eine wichtige Erweiterung sind Lambda-Ausdrücke. Ein Lambda-Ausdruck ist ein Literal, das eine funktionale Schnittstelle implementiert, ohne der Funktion einen Namen zu geben. • Beispiele für Lambda-Ausdrücke, Funktionale Schnittstellen, default-Methoden, ... haben wir schon gesehen Rückblick und Ausblick A-25 Java 8 Auf den nächsten Folien steht etwas zur Wiederholung von Java 8. Rückblick und Ausblick A-26 Lambda-Ausdrücke und funktionale Interfaces Ein Funktionales Interface ist ein normales Interface, das aber nur eine einzige abstrakte Methode enthält. Grundlage der Lambda-Ausdrücke sind funktionale Interfaces. Lambda-Ausdrücke bestehen aus einer Parameterliste und dem Rumpf einer Methode, die durch -> verbunden sind. So können die Lambda-Ausdrücke aussehen: (Liste der Parameter) -> { Befehl; ...; Befehl; } (Liste der Parameter) -> Ausdruck; Die Liste der Parameter kann evtl. auch leer sein. Rückblick und Ausblick A-27 Lambda-Ausdruck und funktionales Interface: Beispiel interface Funktion { double wurzel (double y); } public class Lambda { public static void main(String[] args) { Funktion f = (y) -> Math.sqrt(y); // Lambda-Ausdruck for (double x=0.0; x<=5.0; x++) { double r = f.wurzel(x); System.out.printf("Wurzel von %4.2f: %6.5f%n",x,r); } } } Rückblick und Ausblick A-28 Ausgabe: Wurzel Wurzel Wurzel Wurzel Wurzel Wurzel von von von von von von 0.00: 1.00: 2.00: 3.00: 4.00: 5.00: Rückblick und Ausblick 0.00000 1.00000 1.41421 1.73205 2.00000 2.23607 A-29 Interfaces mit Default- und statischen Methoden Eine Default-Methode ermöglicht es, eine Standardimplementierung in einem Interface zu definieren. Diese Methode muss das Schlüsselwort default bekommen. Eine Standardimplementierung einer Interface-Methode kann auch eine static Methode werden. Diese Methode kann auch schon dann verwendet werden, wenn es noch kein Objekt der Klasse, die das Interface implementiert, gibt. Rückblick und Ausblick A-30 Interface mit Default-Methode: Beispiel interface Funktion { double wurzel (double y); default double quadrat (double x) {return x*x;} } public class Formel implements Funktion { public double wurzel (double y) {return Math.sqrt(y);} public static void main(String[] args) { Formel f = new Formel(); for (double x=0.0; x<=5.0; x++) { double r = f.wurzel(x); System.out.printf("Wurzel von %4.2f: %6.5f%n",x,r); r = f.quadrat(x); System.out.printf("Quadrat von %4.2f: %6.5f%n",x,r); } } } Rückblick und Ausblick A-31 Interface mit statischer Methode: Beispiel interface Funktion { double wurzel (double y); static double hochdrei (double x) {return x*x*x;} } public class Test implements Funktion { public double wurzel (double y) {return Math.sqrt(y);} public static void main(String[] args) { for (double x=0.0; x<=5.0; x++) { double r = Funktion.hochdrei(x); System.out.printf("Quadrat von %4.2f: %8.3f%n",x,r); // Für die statische Methode muss es kein Objekt geben! } } } Rückblick und Ausblick A-32 Funktionen als Parameter: Beispiel interface Funktion { int rechne (int x, int y); } public class Test { public static void anzeigen(Funktion f, Funktion g) { for (int i=0; i<=10; i+=1) { int j = f.rechne(i,i)+g.rechne(i,i); System.out.printf("%4d -> %4d%n",i,j); } } public static void main(String[] args) { // Ausgabe des 5-fachen Wertes anzeigen((int x,int y)->{y=3;return x*y;},(int x,int y)->{return x+y;}); System.out.println(); } } Rückblick und Ausblick A-33 Funktionale Programmierung Wie angekündigt haben wir also aus Java 8 Beispiele für • Lambda-Ausdrücke, • Funktionale Interfaces und • Interfaces mit Default-Methoden und statischen Methoden gesehen. Den Aspekt • Streams und Pipeline-Operationen werden wir später sehen. Rückblick und Ausblick A-34 Closures Friedrich Esser: Ein Closure ist ein Block (bzw. eine Einheit) von Code, der freie Variable aus der Umgebung, genauer dem umgebenden Scope zur Berechnung des Ergebnisses mit einbezieht. Abhängig von der Art der freien Variablen, gibt es Closures, deren – Ergebnisse nur von ihren Argumenten abhängen, da die freien Variablen immutable sind. – Ergebnisse mit den mutablen Werten der freien Variablen variieren. Leider wird Closure je nach Autor oder Sprache evtl. anders interpretiert. Rückblick und Ausblick A-35 Scala • Scala ist eine hybride Sprache: imperativ, objektorientiert, funktional. • SCAlable LAnguage • Scala wird in der Schweiz entwickelt. Leiter: Martin Odersky. • Scala-Programme können Java-Archive (jars) verwenden und umgekehrt. Rückblick und Ausblick A-36 Ist Java 8 der Todesstoß für Scala? Will Java 8 kill Scala? Wir erinnern uns alle noch zu gut: Als Scala damals vorgestellt wurde und die Entwickler-Gemeinde Gefallen an der Sprache fand, unkte man schon, Scala würde Java bald beerben. Und dabei war Scala nicht die einzige JVM-Sprache, die als »Java-Killer« bezeichnet wurde (siehe z.B. Ceylon). Jetzt scheint sich der Spieß allerdings umzudrehen, denn mit Java 8 hat die Programmiersprache dank Lambda-Expressions, Methodenreferenzen, Functional Interfaces und Co. einen enormen Sprung in Richtung mehr Produktivität und Leistungsfähigkeit gemacht. Und schon fragen sich Entwickler wie Ahmed Soliman in seinem Blog: Will Java 8 kill Scala? Oder rücken die beiden Sprachen nicht eher näher zusammen? (Claudia Fröhling, 27. März 2014) Rückblick und Ausblick A-37 Anmerkungen zur Objektorientierung (Wiederholung) Das objektorientierte Paradigma kann anders (und/oder erweitert) als in Java angeboten werden: • keine primitiven Datentypen • keine statischen Attribute, keine statischen Methoden • singleton object, standalone object, case class • companion object, companion class • Mehrfachvererbung • mixin, trait • ... Rückblick und Ausblick A-38 Anmerkungen zur Objektorientierung (Wiederholung) Die Mehrfachvererbung führt (wie schon beschrieben) zum sog. Diamond-Problem. Mixins und Traits ermöglichen eine wiederverwendbare Menge von Attributen und Methoden (ähnlich wie Klassen). Traits sind eine Variante der Mixins. Traits gibt es inzwischen in vielen Programmiersprachen, zum Beispiel Scala. Lothar Piepmeyer: Anders als in Java ist in Scala alles ein Objekt. Die Teilung in primitive Typen und den Rest der Welt kennt Scala nicht. Unter der Haube bildet der Compiler den Scala-Typen Int auf die Java-Typen int und Integer ab. Rückblick und Ausblick A-39 Die wichtigsten der bisherigen Aspekte • Paradigma oder hybrid? • Anweisungen, Sequenz, Auswahl, Wiederholung, Deklaration, break, continue • Datentypsystem, Variable, Konstante, Operatoren, Ausdrücke, Speicherung • Methoden und ihre Signatur • Gültigkeit, Sichtbarkeit, Überladung • Programmiertechniken, z. B. Rekursion • Zuverlässigkeit von Programmen • Ein- und Ausgabe, packages, ... • Weitere Aspekte kommen im nächsten Semester! Rückblick und Ausblick A-40 Programmierausbildung • Imperative, objektorientierte und Grundlagen der Programmierung: ◦ Programmieren I (Java) ◦ Programmieren II (Java, auch kleiner Blick auf andere Sprachen) • Softwaretechnik: ◦ SoftwareEngineering I (UML) ◦ Softwareentwicklungspraktikum (Java oder andere Sprache, UML) • Funktionale Programmierung und Allgemeine Sicht auf Programmiersprachen: ◦ Programmieren für Fortgeschrittene Rückblick und Ausblick (Haskell) A-41 Programmierausbildung • Deduktive Programmierung und Weiteres zur Logik: ◦ Logik in der Informatik (Prolog) • Bedeutung von Programmen: ◦ Semantik von Programmiersprachen • Implementierung von Programmiersprachen: ◦ Compiler I, II ◦ Compilerbaupraktikum • Entwicklung von Software für eine konkrete Anwendung: ◦ Softwaretechnisches Industriepraktikum Rückblick und Ausblick A-42 Programmieren I + II Programmieren I: 6 LP, 2+2 (+ Rechnerübungen), im Wintersemester, ab SS2016: auch im Sommersemester. Inhalt: Grundzüge der imperativen, objektorientierten und funktionalen Programmierung. Java. Programmieren II: 6 LP, 2+2 (+ Rechnerübungen), im Sommersemester, ab WS2016: auch im Wintersemester. Inhalt: Vertiefung der objektorientierten Programmierung, auch kleiner Blick auf andere Sprachen. Schwerpunkte: Programmierung von Datenstrukturen, Parallelprogrammierung und Grafikprogrammierung. Prüfungsvorleistung/Studienleistung (je nach Studiengang): Hausaufgaben Prüfung: Klausur Rückblick und Ausblick A-43 Gehirn und Geist In der Ausgabe Februar 2015 (Monatsheft) der Zeitschrift Gehirn und Geist erschien ein Artikel mit dem Titel „Im Kopf des Programmierers“. Zitat: Während Laien im Quelltext von Computerprogrammen meist nur kryptische Zeichen sehen, entschlüsseln Informatiker im Handumdrehen die Bedeutung des Kodes. Forscher untersuchen, was dabei im Gehirn passiert. von Janet Siegmund, Sven Apel und André Brechmann. Rückblick und Ausblick A-44