Algorithmik II 1. Übung Organisatorisches: Die Termine der Übungen, die Übungsblätter und weitere Informationen sind auf der Webseite der Übungen zu finden: http://www8.informatik.uni-erlangen.de/IMMD8/Lectures/SS03/algo2/uebungen.html Jeder Studierende muß sich zu einer Tafel- und einer Rechnerübung anmelden. Die Anmeldungen zu den Übungen ist verbindlich! Die Anmeldung zu den Übungen erfolgt ¨uber das Web-AnmeldeSystem “WAS” http://www.was.dienste.uni-erlangen.de Der Übungsbetrieb beginnt am Montag den 14. April 2003. Aufgabe 1 a) Erklären Sie die Unterschiede zwischen anweisungs-, funktions-, regel- und objektorientierter Programmierung. Lösungsvorschlag: 14. – 25. April 2003 -1- Sommersemester 2003 Algorithmik II 1. Übung anweisungsorientierte Programmierung zentrale Sprachelemente Variablen und Anweisungen, die den Zustand der Variablen ändern Programm Sequenz von Anweisungen Programmausführung 14. – 25. April 2003 funktionsorientierte Programmierung Funktionen und ihre Anwendung auf Parameter Ansammlung nebeneinander stehender Funktionsdefinitionen Ausführung der Auswertung der Anweisungen Programmfunk” in der notierten tion“, wobei Reihenfolge. hierzu evtl. noch Beim Erreichen viele weitere einer Sprungan- Funktionen ausweisung wird gewertet werden abhängig von müssen. aktuellen Variablenwerten entschieden, welche Anweisung als nächste ausgeführt wird. -2- regelorientierte Programmierung objektorientierte Programmierung Logische Folgerung Objekte, die einen inneren Zustand und Methoden haben, mit denen dieser Zustand geändert werden kann. Jedes Objekt hat einen bestimmten Typ. Ansammlung von Ansammlung von Fakten und Re- Objekten geln Ableitung neuer Aussagen aus bereits bekannten; Versuch, eine Anfrage zu verifizieren Objekte schicken einander Nachrichten, indem sie die Methoden der anderen Objekte ansprechen Sommersemester 2003 Algorithmik II 1. Übung b) Welche dieser Konzepte finden sich in den Programmiersprachen Scheme und Java? Lösungsvorschlag: Scheme: • Funktionsorientierte Programmierung: In Scheme ist alles eine Funktion. Es stehen mehrere Funktionsdefinitionen nebeneinander. Ein Programm wird gestartet, indem eine Funktion ausgeführt wird. • Anweisungsorientierte Programmierung: Zustandsänderungen von Variablen durch den set!-Befehl, z.B. (set! summe (+ summe wert)), oder append! Java: • Anweisungsorientierte Programmierung: Ausführung von Anweisungen in der notierten Reihenfolge, Sprunganweisungen (do/while) • Objektorientierte Programmierung: Objekte können andere Objekte ansprechen durch Aufruf der Methoden der anderen Objekte Auch in Scheme lassen sich objektorientierte Konzepte realisieren. Sie sind jedoch nicht Teil des Sprachumfangs. Aufgabe 2 Schreiben Sie ein Java-Programm zur Berechnung der Fakultätsfunktion. Schreiben Sie sowohl eine rekursive als auch eine iterative Methode. Lösungsvorschlag: • Rekursive Fassung: int fakRek(int n) { if (n <= 1) { return 1; } else return n * fakRek(n-1); } • Iterative Fassung mit abweisender Schleife: int fakIter(int n) { int i = 1; int ergebnis = 1; while (i <= n) { ergebnis *= i; i++; } return ergebnis; } 14. – 25. April 2003 -3- Sommersemester 2003 Algorithmik II 1. Übung • Iterative Fassung mit nichtabweisender Schleife: int fakIter2(int n) { int i = 1; int ergebnis = 1; do { ergebnis *= i; i++; } while (i <= n); return ergebnis; } Je nach Wissensstand der Übungsteilnehmer kann hier auch darauf eingegangen werden, daß in Scheme (Abelson/Sussman, Kapitel 1.21) zwischen rekursiven Prozeduren und rekursiven Prozessen unterschieden wurde. Ein iteratives Programm“ in Scheme ” ist ein rekursives Programm, das einen iterativen Prozeß erzeugt. In Java dagegen werden iterative Prozesse durch nicht-rekursive Methoden erzeugt, indem Schleifen verwendet werden. Im Gegensatz zu Scheme gilt für Java, daß eine Java-Methode noch kein vollständiges, lauffähiges Java-Programm ist. Um ein solches zu erhalten, ist mindestens folgendes notwendig: • Die Methode muß in eine Klasse eingebettet werden. • Man braucht weiterhin eine Klasse mit einer Methode main. Die Ausführung eines Java-Programms beginnt immer mit der Methode main der als public deklarierten Klasse. Jedes Java-Programm muß eine solche Klasse enthalten. In der main-Methode werden Objekte dieser oder auch anderer Klassen erzeugt und deren Methoden aufgerufen. Die Klassendefinitionen müssen entweder im Programm vorhanden sein oder aus Bibliotheken übernommen werden. Ein vollständiges Programm um die obigen Methoden herum könnte z.B. so aussehen: public class Fakultaet { int fakIter(int n) { ... } int fakRek(int n) { ... } public static void main(String[] args) { int n = 5; Fakultaet f = new Fakultaet(); System.out.print("Rekursiv: " + n + "! ist "); System.out.println(f.fakRek(n) + "."); System.out.print("Iterativ: " + n + "! ist "); 14. – 25. April 2003 -4- Sommersemester 2003 Algorithmik II 1. Übung System.out.println(f.fakIter(n) + "."); } } Aufgabe 3 Ablaufstrukturen Realisieren Sie die nachfolgend angegebene Funktion sowohl als Struktogramm als auch in Java. a2 + (a + 1)2 + . . . + (b − 1)2 + b2 f (a, b) = a2 2 b + (b + 1)2 + . . . + (a − 1)2 + a2 falls a < b falls a = b falls b < a Lösungsvorschlag: Mit Hilfe von Struktogrammen kann man einen Algorithmus zunächst einmal unabhängig von der Syntax einer bestimmten Programmiersprache formulieren. int sum_quad (int a, int b) summe = 0 solange a kleiner oder gleich b Erhöhe summe um a*a Erhöhe a um 1 Gib summe zurück int f(int a, int b) Ist a gleich b ? Nein Ja Ist a kleiner als b ? Gib a*a zurück Ja Nein Gib sum_quad(a,b) zurück Gib sum_quad(b,a) zurück int sumQuad(int a, int b) { int summe = 0; while (a <= b) { summe += a * a; a++; } 14. – 25. April 2003 -5- Sommersemester 2003 Algorithmik II 1. Übung return summe; } int f(int a, int b) { if (a == b) return a*a; else if (a < b) return sumQuad(a, b); else return sumQuad(b, a); } Aufgabe 4 Wertzuweisung Gegeben sei folgendes Java-Programm: class UnsereKlasse { int Komponente; } public class KomponentenTester { public static void main (String[] args) { UnsereKlasse Objekt1 = new UnsereKlasse (); UnsereKlasse Objekt2 = new UnsereKlasse (); 01 02 03 04 05 06 Objekt1.Komponente Objekt1.Komponente Objekt2.Komponente Objekt2 = Objekt1; Objekt2.Komponente Objekt1.Komponente = 10; Objekt2.Komponente = 20; = Objekt2.Komponente; = 30; 07 Objekt1 = new UnsereKlasse (); Objekt1.Komponente = 60; = 40; = 50; } } Gehen Sie das Programm Zeile für Zeile durch und ermitteln Sie den jeweiligen Wert von Objekt1.Komponente und Objekt2.Komponente. Lösungsvorschlag: Zeile Objekt1.Komponente 01 10 02 20 03 20 04 20 05 40 06 50 07 60 14. – 25. April 2003 Objekt2.Komponente 20 20 30 20 40 50 50 -6- Sommersemester 2003 Algorithmik II 1. Übung Rechnerübung 1 Arbeiten mit Java unter Linux Um mit dem JDE (Java Development Environment) arbeiten zu können, müssen zunächst folgende Zeilen in die Datei .emacs eingetragen werden: (setq load-path (nconc ’( "/local/jde/lisp" ) load-path)) (require ’jde) Java–Programme können nicht unmittelbar gestartet werden. Sie müssen erst mit Hilfe eines Java– Compilers übersetzt werden: Dazu muß in der Menüleiste unter dem Menü JDE das Kommando Compile aufgerufen werden. Der Java–Compiler erzeugt damit im gleichen Verzeichnis eine ausführbare JAVA-Datei Ggt.class. Die class-Datei kann nun mit dem Kommando Run App unter dem Menü JDE ausgeführt werden. a) Erstellen Sie zunächst im emacs eine Datei Ggt.java mit folgendem Inhalt: public class Ggt { public int ggtRekursiv (int a, int b) { if( b == 0 ) return a; else { if ( b > a ) return ggtRekursiv(b, a); else return ggtRekursiv(b, a-b); } } public static void main (String[] args) { Ggt ggtBeispiel = new Ggt(); System.out.println("Der ggT von " + 44 + " und " + 55 + " ist: " + ggtBeispiel.ggtRekursiv(44, 55)); } } 14. – 25. April 2003 -7- Sommersemester 2003 Algorithmik II 1. Übung b) Übersetzen Sie den Inhalt der Datei Ggt.java in ein ausführbares Java–Programm! Lösungsvorschlag: Aufruf des Kommandos Compile in der Menüleiste unter dem Menü JDE. c) Führen Sie das übersetzte Programm aus! Wählen Sie andere Zahlenwerte und übersetzen Sie jeweils das Programm erneut, um es wieder auszuführen. d) Es ist lästig, daß die gewünschten Zahlen im Quellcode eingetragen werden müssen. Erweitern Sie daher die main–Funktion um folgende Zeilen: BufferedReader console = new BufferedReader( new InputStreamReader(System.in)); String text1 = new String(""); String text2 = new String(""); try { System.out.println("Zahl 1 eingeben:"); text1 = console.readLine(); System.out.println("Zahl 2 eingeben:"); text2 = console.readLine(); } catch (IOException e) {}; int zahl1 = Integer.parseInt(text1); int zahl2 = Integer.parseInt(text2); und fügen Sie am Beginn import java.io.*; ein. Es werden nun zwei Strings eingelesen. Die try-catch-Befehle dienen dazu, Probleme beim Einlesen der Zahlen von der Konsole abzufangen. Sie sollen uns im Moment nicht weiter interessieren. Aus den eingelesenen Strings werden im Anschluß die beiden int-Zahlen ermittelt. Verändern Sie nun die Zeile mit dem Ausgabebefehl so, daß die ggT–Berechnung mit den eingegebenen Zahlen ausgeführt wird. Übersetzen Sie das Programm erneut und führen Sie es mit einigen von Ihnen selbst gewählten anderen Zahlenwerten aus. Lösungsvorschlag: System.out.println("Der ggT von " + zahl1 + " und " + zahl2 + " ist: " + ggtBeispiel.ggtRekursiv(zahl1, zahl2)); e) Ändern Sie das Java–Programm in Ihrer Datei Ggt.java so ab, daß bei der Eingabe negative Zahlen erkannt und zurückgewiesen werden. Das Programm soll so lange erneut zwei Zahlen einlesen, bis vom Benutzer korrekt zwei positive Zahlen eingegeben wurden. Denken Sie daran, daß das Programm korrekt beendet oder abgebrochen worden sein muß, bevor Sie erneut Run App im Menü JDE aufrufen, da sonst der alte Prozeß fortgesetzt wird. 14. – 25. April 2003 -8- Sommersemester 2003 Algorithmik II 1. Übung Wollen sie das Programm abbrechen, erreichen Sie dies durch zweimaliges Drücken der Tastenkombination Control-C im Ausführungsfenster. Lösungsvorschlag: Die Funktion main könnte wie folgt reformuliert werden: public static void main (String[] args) { boolean eingabeOk = false; BufferedReader console = new BufferedReader( new InputStreamReader(System.in)); String text1 = new String(""); String text2 = new String(""); while ( !eingabeOk ) { try { System.out.println("Zahl 1 eingeben:"); text1 = console.readLine(); System.out.println("Zahl 2 eingeben:"); text2 = console.readLine(); } catch (IOException e) {}; int zahl1 = Integer.parseInt(Text1); int zahl2 = Integer.parseInt(Text2); if( zahl1 < 0 || zahl2 < 0 ) { System.out.println("Bitte geben Sie zwei positive ganze Zahlen ein!"); } else { Ggt ggtBeispiel = new Ggt(); System.out.println("Der ggT von " + zahl1 + " und " + zahl2 + " ist: " + ggtBeispiel.ggtRekursiv(zahl1, zahl2)); eingabeOk = true; } } } f) Modifizieren Sie das Java–Programm aus Ggt.java so, daß nicht für jedes Zahlenpaar ein erneuter Programmaufruf notwendig ist. Dies erreichen Sie zum Beispiel, indem Sie nach der Ausgabe des Ergebnisses den Benutzer fragen, ob das Programm beendet werden soll. 14. – 25. April 2003 -9- Sommersemester 2003 Algorithmik II 1. Übung Hinweis: Strings können in Java mit der Methode boolean equals(String) auf Gleichheit überprüft werden (s.h. auch Java API-Spezifikation). Lösungsvorschlag: Die Funktion main könnte wie folgt erweitert werden: do { /* Eingabe der Zahlen */ ... /* Ausgabe des Ergebnisses */ ... System.out.println( "Wollen Sie das Programm beenden? "); try { eingabeString = console.readLine(); } catch (IOException e) {}; while ( !eingabeString.equals("ja") && !eingabeString.equals("j") ); Das ist keine vollständige main–Funktion. Es ist hier nur angegeben, wie obiges Programm um die Abfrage erweitert werden könnte. g) Ergänzen Sie nun die Klasse Ggt um eine Methode ggtIterativ, die den ggT iterativ berechnet und führen Sie die Berechnung mit dieser Methode durch. Lösungsvorschlag: public int ggtIterativ (int a, int b) { int x,y; x = a; y = b; while (x != 0 && y != 0) { if(x > y) x = x - y; else y = y - x; } return (x + y); } h) Spielen“ Sie mit weiteren Java–Programmen aus der Vorlesung oder der Tafelübung. ” 14. – 25. April 2003 - 10 - Sommersemester 2003