Strukturiertes Programmieren

Ähnliche Dokumente
Übung zur Vorlesung Strukturiertes Programmieren WS 2014/15. Übungsblatt 1: JAVA - Erste Schritte Abgabe: Besprechung:

Programmierkurs Java

Informatik B von Adrian Neumann

Java 8. Elmar Fuchs Grundlagen Programmierung. 1. Ausgabe, Oktober 2014 JAV8

Programmierung WS12/13 Lösung - Übung 1 M. Brockschmidt, F. Emmes, C. Otto, T. Ströder

Hochschule Augsburg, Fakultät für Informatik Name:... Prüfung "Programmieren 1", IN1bac, WS 10/11 Seite 1 von 6

Rekursive Funktionen

Tutoraufgabe 1 (Programmanalyse):

Praktikum zu Einführung in die Informatik für LogWiIngs und WiMas Wintersemester 2015/16. Vorbereitende Aufgaben

Probeklausur: Programmierung WS04/05

Übungen zur Vorlesung Einführung in die Informatik Wintersemester 2010/11

Einstieg in die Informatik mit Java

Programmieren lernen mit Groovy Rekursion Rekursion und Iteration

Wiederholung Wozu Methoden? Methoden Schreiben Methoden Benutzen Rekursion?! Methoden. Javakurs 2012, 3. Vorlesung

Einstieg in die Informatik mit Java

PIWIN 1 Übung Blatt 5

Praktikum zu Einführung in die Informatik für LogWiIngs und WiMas Wintersemester 2015/16. Vorbereitende Aufgaben

II.3.1 Rekursive Algorithmen - 1 -

Javakurs für Anfänger

Einfache Arrays. Annabelle Klarl. Einführung in die Informatik Programmierung und Softwareentwicklung

Java: Vererbung. Teil 3: super()

Aufgabenblatt Nr. 5 Generizität und TicTacToe

Objektorientierte Programmierung

5. Tutorium zu Programmieren

Kapitel 6. Vererbung

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

Grundlagen Programmierung

Probeklausur: Programmierung WS04/05

Fachgebiet Informationssysteme Prof. Dr.-Ing. N. Fuhr. Programmierung Prof. Dr.-Ing. Nobert Fuhr. Übungsblatt Nr. 6

Speicher und Adressraum

JAVA - Methoden - Rekursion

Kapitel 6. Vererbung

II. Grundlagen der Programmierung. 9. Datenstrukturen. Daten zusammenfassen. In Java (Forts.): In Java:

Java Einführung Abstrakte Klassen und Interfaces

5 Codierung nach RSA (Lösung)

Kapitel 6. Vererbung

Prof. Dr. Heinrich Müller; Dr. Frank Weichert 7. September 2015

Versuchsziele Kenntnisse in der Anwendung von: Sortieren mit Klassen Benutzung von generischen Klassen o Definition o Sortierung.

Ich liebe Java && Ich liebe C# Rolf Borst

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

Einführung in die Java- Programmierung

Programmentwicklung I für Hörer anderer Fachrichtungen -Wintersemester 2003/04- Abschlussklausur

JAVA - Methoden

Java I Vorlesung Vererbung und Sichtbarkeit

Repetitorium Informatik (Java)

Tutoraufgabe 1 (2 3 4 Bäume):

368 4 Algorithmen und Datenstrukturen

Klausur zur Einführung in die objektorientierte Programmierung mit Java

1. Typen und Literale (6 Punkte) 2. Zuweisungen (6 = Punkte)

Javakurs für Anfänger

Einführung in die Java- Programmierung

Das erste Programm soll einen Text zum Bildschirm schicken. Es kann mit jedem beliebigen Texteditor erstellt werden.

4 Codierung nach Viginere (Lösung)

Multimedia im Netz Wintersemester 2011/12

1 Polymorphie (Vielgestaltigkeit)

Übungen zu Programmierung I - Blatt 8

Praktikum Ingenieurinformatik. Termin 4. Funktionen, numerische Integration

public class SternchenRechteckGefuellt {

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

Einführung in die Programmierung WS 2009/10. Übungsblatt 9: Objekt-Orientierung

Selbsteinstufungstest Vorkurs Programmieren

Strukturiertes Programmieren

Aufgabe 1. »Programmieren«, WS 2006/2007. Nino Simunic M.A.

Objektorientierte Programmierung

Kapitel 7 des Buches, von Java-Selbstbau nach Scala-Library portiert Christoph Knabe

Tutoraufgabe 1 (Programmanalyse):

Prof. H. Herbstreith Fachbereich Informatik. Leistungsnachweis. Informatik 1 WS 2001/2002

Die Programmiersprache C Eine Einführung

Java Einführung Collections

Programmieren in Java

Gliederung. Tutorium zur Vorlesung. Gliederung. Gliederung. 1. Gliederung der Informatik. 1. Gliederung der Informatik. 1. Gliederung der Informatik

Vorlesung Programmieren

Klausur zur Einführung in die objektorientierte Programmierung mit Java

Einführung in die Java- Programmierung

Übung 1: Object Inspector

BTree.dll - Balancierte und verkettete Bäume. Ecofor. BTree.dll. Realisiert mit Microsoft Visual Studio /9

Welche Informatik-Kenntnisse bringen Sie mit?

Übungen zur Vorlesung EidP (WS 2015/16) Blatt 6

Javakurs zu Informatik I. Henning Heitkötter

Einführung in die Programmierung

JavaScript. Dies ist normales HTML. Hallo Welt! Dies ist JavaScript. Wieder normales HTML.

CS1005 Objektorientierte Programmierung Bachelor of Science (Informatik)

Präsentation Interfaces

Einführung in die Programmierung

Grundlagen Programmierung

Einführung in die Informatik 1

6. Iteration (Schleifenanweisungen)

Themen. Web Service - Clients. Kommunikation zw. Web Services

Tag 7. Pattern Matching und eigene Datentypen

Fortgeschrittene Programmiertechnik Klausur SS 2015 Angewandte Informatik Bachelor

Vorlesung Datenstrukturen

Tutorium - Haskell in der Schule. Ralf Dorn - Dennis Buchmann - Felix Last - Carl Ambroselli

1 Das Prinzip der vollständigen Induktion

Einführung in die Programmierung für Wirtschaftsinformatik

Institut für Programmierung und Reaktive Systeme 26. April Programmieren II. 10. Übungsblatt

Transkript:

Friedrich-Schiller-Universität Jena Fakultät für Mathematik und Informatik Institut für Informatik Prof. Dr. E.-G. Schukat-Talamazzini http://www.minet.uni-jena.de/fakultaet/schukat/ Prof. Dr. P. Dittrich http://users.minet.uni-jena.de/~dittrich/, R3430 Übung zur Vorlesung Strukturiertes Programmieren WS 2016/17 Übungsblatt 13: Namenlose Funktionen, Lambda, Streams Abgabe: 23.01.2016 Besprechung: 24.01.2016 Allgemeine Hinweise: 1. Bitte bringen Sie den Quelltext Ihrer Lösungen zur Übung mit. 2. Abgabe auch möglich bis 13:00 (Montags) Büro Dittrich (EAP 1-4, 4. Stock, rechts, ganz hinten). 3. Bearbeitungszeit: 10h 4. Maximale Punktzahl: 15 Punkte 5. small Abgegebene Blätter tackern. Gruppennummer notieren (oben rechts). Skript zu jeder Veranstaltung mitbringen. Ziele: 1. Umgang mit namenlosen Funktionen (Lambda, Closures) üben. 2. Streamorientierte Programmierung kennenlernen; ein ultra-mächtiges Konzept, das sich überall findet. 3. Das Konzept der verzögerten Auswertung (delay, force, lazy evaluation) kennenlernen und mit Hilfe von lambdas realisieren. 4. Rekursion anwenden lernen. Bemerkungen: In dieser Übung werden wir anhand der streamorientierten Programmierung die Verwendung namenloser Funktionen ( lambdas ) und die Verwendung von Rekursion einüben. Streams haben Sie schon indirekt als Eingabe- und Ausgabestreams genutzt. Datenstromorientiertes Design findet man aber auch anderswo, bspw. bei Vektorrechnern und auf Grafikkarten. Auch wenn Sie beispielsweise schreiben ls less werden Streams verwendet, um die beiden Programme ls und less miteinander zu verbinden. Mit Streams lassen sich einige Probleme sehr elegant lösen. Achtung: Die Streams, die wir hier implementieren, sind anders als die JAVA Streams! Unsere Streams werden in gewissen Punkten mächtiger sein. Literatur: Ableson/Sussman,Structure and Interpretation 1

1 Wiederholung (1h / 1P) Arbeiten Sie das Skript (Folien) zur laufenden Vorlesung durch; insbesondere die Teile, die nicht in der Übung behandelt werden; etwa: Endrekursion Alles klar soweit? Was ist unklar geblieben? Investierte Zeit neben der Vorlesung: Zusammengearbeitet mit: 2 Mit Lambdas warmwerden (3h / 6P) a) Suchen Sie in Ihrem Buch oder im Netz (z.b. auf oracle.com) nach einer für Sie geeigneten Einführung in die namenlosen Funktionen von JAVA 8 ( JAVA lambda ) und lesen Sie kurz (!) hinein. Verwenden Sie zunächst aber NICHT die Funktionsdefinitionen (functional interface) der JAVA Bibliothek. Implementieren Sie die benötigten functional Interfaces (Datentypen der lambdas, z.b. Function) selbst! Hier ein einfaches Beispiel: Zunächst die Definition des functional interface public interface Function { double eval(double x); Und nun eine Anwendung, nämlich eine Methode, die eine Funktion f und eine Zahl x als Parameter hat und den Funktionswert f(x) ausgibt: public static void printfunctionvalue(function f, double x){ System.out.println(f.eval(x)); public static void main(string[] args) { printfunctionvalue(x -> x * x, 10 ); Function f = x -> x + 2; printfunctionvalue(f, 20); b) Schreiben Sie eine Methode applyandprint, die zwei Zahlen i, j und eine Funktion f : N N als Parameter hat und diese Funktion auf die Zahlen i, i + 1,..., j anwendet. Implementieren Sie dazu auch ein functional interface Function. Zum Beispiel für i = 2, j = 5 und f(i) = i i: applyandprint(2,5, <QuadratischeFunktion>); AUSGABE: 4 9 16 25 applyandprint(2,5, <EineAndereFunktion>); AUSGABE: 8 27 64 125 2

c) Schreiben Sie eine Methode iterate, mit folgenden Eingaben: eine Funktion f : R R, ein Startwert x 0 R und eine Iterationsanzahl n N. Die Funktion f soll nun wiederholt wie Folgt angewendet werden x i+1 = f(x i ), mit i = 0, 1,..., n und dabei x i geeignet ausgeben. d) Testen Sie iterate mittels: f(x) = 2x (exponentielles Wachstum), f(x) = 0.5x (Zerfall), und f(x) = αx(x 1) (logistische Abbildung) für verschiedene alpha etwa α = 0.5, 1.5, 2.5, 3.5, 3.57, 3.7, 3.82. Startwerte x 0 sollten zwischen null und eins liegen. Für die Abgabe nur ein interessantes Ergebnis (mit Angabe der Parameter) ausdrucken. e) Optional, wenn Sie die anderen Aufgaben bearbeitet haben, können Sie die x i auch geeignet plotten. (Bildersuche nach logistischer Abbildung ). 3 Einfache Streams (2h / 3P) Streams sind Listen ähnlich. In einfachster Form können sie über folgendes Interface definiert werden: theemptystream: -> Stream // liefert den leeren Stream isempty: Stream -> boolean // wahr, falls der stream leer. newstream: Item x Stream -> Stream // erweitert einen Stream um ein item head: Stream -> Item // liefert das naechste item des Streams tail: Stream -> Stream // liefert den Rest des Streams Zur Semantik (wie bei der Liste): head(newstream(i, s)) = i tail(newstream(i, s)) = s isempty(theemptystream()) = true isempty(newstream()) = false Das sieht aus wie eine Liste (die bis hierher genauso definiert wird). Eine Liste können wir also soweit verwenden, um einen Stream zu implementieren. Streams gehen aber in einigen Punkten über Listen hinaus: In der Regel sind Funktionen höherer Ordnung verfügbar, die es erlauben Funktionen auf Streams anzuwenden. Diese Funktionen höherer Ordnung sind so geschaffen, dass wir diese elegant verbinden können. Ferner können Streams implizit (generativ) definiert werden, was sehr lange oder gar unendlich lange Streams erlaubt (dies aber erst in Aufgabe 4). Ein Beispiel: Wir wollen die Summe der Quadratzahlen von 4, 5,.., 20, die durch 2 teilbar sind, berechnen. Ein Streamorientiert Ansatz würde eine Stream von Integer von 4 bis 20 erzeugen, diesen quadieren und das Ergebniss aufaddieren. Das könnte dann so aussehen: SimpleStream<Integer> s1 = streamofinteger(4, 20); SimpleStream<Integer> s2 = s1.map(x -> x*x); SimpleStream<Integer> s3 = s2.filter(x -> x % 2 == 0); Integer sum = s3.sumup(); ALTERNATIV: Integer sum = streamofinteger(4,20).map(x->x*x).filter(x -> x % 2 == 0).sumup(); 3

a) Implementieren Sie einen einfachen Stream mit elementaren Mitteln, der obiges erlaubt (d.h. keine JAVA Klassen aus dem Collection-Framework verwenden). b) Verwenden Sie zunächst NICHT die Funktionsdefinitionen der JAVA Bibliothek. Definieren Sie vielmehr die Datentypen (Interface) für die benötigten Funktionen selbst. Wir benötigen: Predicate<T>: pred -> boolean Supplier<T>: -> T BiFunction<T>: T x T -> T // ist in JAVA allgemeiner def. Für: public SimpleStream<T> filter(predicate<t> pred) c) Implementieren und testen Sie eine Methode join, die zwei Streams paarweise vereinigt und wieder einen Stream liefert. Implementieren Sie auch eine geeignete Methoden tostring() und tostring(int n) für Ihren Stream, damit Sie sie ausgeben können. public SimpleStream<T> join(simplestream<t>, BiFunction<T>) BEISPIEL: zwei streams (1,2,3,4) und (2,3,4,5,6) paarweise addieren und ausgeben: System.out.println(streamOfInteger(1,4).join(streamOfInteger(2,6), (x,y)-> x+y)); AUSGABE: 3 5 7 9 6 Hinweis: Testen Sie schrittweise die implementierten Methoden. Rahmen für den SimpleStream : public class SimpleStream<T> { T head; SimpleStream tail; SimpleStream(T i, SimpleStream s) { head = i; tail = s; SimpleStream() { // = theemptystream tail = null; public boolean isempty() { public T gethead() { public SimpleStream gettail() { @Override public String tostring() { public String tostring(int n) { public static SimpleStream theemptystream(){ return new SimpleStream(); // not really nice to create every time a new object 4

public static SimpleStream streamofinteger(integer from, Integer to){ public SimpleStream<T> filter(predicate<t> pred). join sumup 4 Unendlich lange Streams - Das Problem (1h / 2P) So, jetzt wird s spannend. Nun wollen wir unendlich lange Streams mit endlicher Rechenpower realisieren. Als Beispiel werden wir am Ende das Sieb des Eratosthenes implementieren. Doch zunächst Schritt für Schritt: Schön wäre es, wenn wir den unendlich langen Stream von Einsen wie folgt definieren könnten: SimpleStream<Integer> ones = new SimpleStream(1, ones); SimpleStream<Integer> integers = new SimpleStream(1, integers.map(x -> x+1)); a) Warum geht das nicht? (ausprobieren) b) Warum geht auch folgendes nicht? Obwohl es syntaktisch korret ist und es auch die gleiche Form hat, wie unsere Methode, die einen Stream von integers streamofinteger(from, to) erzeugt. static SimpleStream<Integer> ones() { return new SimpleStream<Integer>(1, ones() ); static SimpleStream<Integer> integers(integer from) { return new SimpleStream<Integer>(1, integers(from+1)); 5 Streams mit lazy tails - Realisierung mit lambda (3h / 6P) Der fundamentale Trick besteht nun darin, dass wir bei der Konstruktion eines neuen Streams, die Auswertung des Rests des Streams verzögern und nur ein Versprechen speichern, mit Hilfe dessen wir bei Bedarf den Rest (tail) des Streams berechnen können. Konkreter bedeutet dies für unser Beispiel, dass wir die weitere Berechnung der Einsen verzögern wollen, etwa so (klappt so natürlich noch nicht in JAVA): static Stream<Integer> ones() { return new Stream<Integer>(1, DELAY{ones() ); Durch DELAY soll verhindert werden, dass ones() ausgewertet wird. Vielmehr soll DELAY ein Versprechen erzeugen, wie die restlichen Einsen berechnet werden sollen. 5

class Stream<T> { tailversprechen Stream(T item, s){ tailversprechen = s; Stream<T> tail() { return FORCE(tailVersprechen); DELAY{BODY wollen wir in JAVA dadurch realisieren, dass wir eine namenlose Funktion (ohne Parameter) erzeugen. FORCE entspricht dann einfach der Auswertung dieser namenlosen Funktion. Damit lässt sich der Stream von Einsen wie folgt definieren: static Stream<Integer> ones() { return new Stream<Integer>(1, ()-> ones() ); Und der Stream aller Integers die bei i starten: static Stream<Integer> integers(integer from) { return new Stream<Integer>(1, ()-> integers(from+1)); Ist das nicht wunderschön? Jetzt können wird das Sieb des Eratosthenes definieren (Wow!): public static Stream<Integer> sieve(stream<integer> stream) { return new Stream<Integer>( stream.gethead(), () -> sieve(stream.gettail().filter( i -> i % stream.gethead()!= 0) )); a) Implementieren Sie die Klasse Stream und testen Sie die verschiedenen Methoden geeignet (z.b. Stream aller Quadratzahlen erzeugen und die ersten 5 Elemente ausgeben). Und führen Sie das Sieb wie oben aus. Nennen Sie den Datentyp der eine null-stellige Funktion repräsentiert Supplier. Definieren Sie selbst das entsprechende functional interface (nicht die JAVA Bib. verwenden). b) Ersetzen Sie die Zeile: i -> i % stream.gethead()!= 0) durch: notdivisible(i, stream.gethead())). c) Generieren Sie den Stream der Fibonacci-Zahlen. Es gibt grundsätzlich zwei Varianten, die sehr unterschiedliche Laufzeiten haben. Versuchen Sie beide zu finden. Eine genügt aber. 6

6 Anwendungsbeispiel Surf-Robot (OPTIONAL) (5h / 10P) Bemerkung: Diese Übung ist aufwendig, also nur im Notfall bearbeiten. Entwickeln Sie einen Surf-Roboter, der ausgehend von einer Start-URL, das Internet nach weiteren Servern durchsucht. a) Schreiben Sie eine Funktion Stream<String> makestringstream(string data, String separator), die einen Datenstring in Teilstrings zerlegt (bezüglich des Seprators) und diese zu einem Stream zusammen fügt. Beispielsweise: System.out.println(makeStringStream("aaxbbxccxeex","x"); AUSGABE: aa bb cc ee Mit Hilfe der Klasse In und ihrer Methode readall() der stdlib können Sie eine Webseite als String einlesen. b) Gegeben eine URL, generieren Sie einen Stream von Links. Verwenden Sie dazu als Separator " und filtern Sie die Strings heraus, die mit http beginnen. c) Druchsuchen Sie nun rekursiv das Netz mit Hilfe der neuen URLs. Das können Sie klassisch machen oder elegant, indem Sie den Stream rekurive konstruieren und dabei auch schon besuchte Seiten herausfiltern. (Das müsste ähnlich dem Primzahlensieb funktionieren). Versuchen die Suche so durchzuführen, dass möglichst viele Server gefunden werden (knifflig). 7 Vergleich mit JAVA Streams und weitere Experimente mit Streams (OPTIONAL) (a) Sie können jetzt einen Blick auf die JAVA Streams werfen. Probieren Sie ein paar Beispiele aus und versuchen Sie das Sieb des Eratosthenes mit den JAVA Streams so elegant wie oben zu implementieren. (b) Mit Streams kann man viele verückte Sachen machen. Versuchen Sie es. Viel Spaß und Erfolg! 7