Programmiertechnik I
Dr. Martin v. Löwis
Wintersemester 11/12
Die mündlichen Prüfungen finden am 13.2., 14.2. (je A-1.2), 1.3., 2.3., 5.3. (je A-2.1), 26.3., 27.3., 28.3., 29.3. und 30.3.2012 (je HE-52) statt.
Ab sofort ist die Online-Anmeldung möglich.
Die Donnerstagsvorlesungen finden ab sofort in Hörsaal 1 statt.
Die nächste Übung findet am 2.2. statt.
Literatur
- Gumm, Sommer. Einführung in die Informatik. Oldenburg Verlag, 2004
- v. Löwis, Fischbeck. Python 2. Addison-Wesley, 2000
- Barnes, Michael Kölling. Objects First with Java - A
Practical Introduction using BlueJ. Prentice Hall, 2004
- Broy. Informatik . Eine grundlegende Einführung. Band 1,
Springer 1998
- Balzert. Lehrbuch Grundlagen der Informatik. Elsevier 2005
- Futschek. Programmentwicklung und Verifikation. Springer, 1998
Vorlesungen
- Einführung (14.10.2011 18:51:17)
- Darstellung von Informationen (17.10.2011 12:49:58)
- Darstellung von Zahlen (07.11.2011 17:19:39)
- Darstellung von Text (07.11.2011 17:17:24)
- Darstellung von anderen Daten (07.11.2011 17:15:27)
- Programmiersprachen (07.11.2011 17:22:16)
- Spezifikationen, Algorithmen, Programme (07.11.2011 17:16:59)
- Formale Beschreibung von Programmiersprachen (24.11.2011 12:26:25)
- Erweiterte Kontrollflusskonzepte (24.11.2011 12:29:00)
- Unterprogramme (28.11.2011 10:56:00)
- Ausnahmebehandlung (28.11.2011 10:52:34) (Beispiel 1, Beispiel 2)
- Konstruktion neuer Datentypen (05.12.2011 11:18:30)
- Verifikation (05.01.2012 12:21:36)
- Versionsverwaltung (05.01.2012 12:19:43)
- Objekt-orientierte Programmierung (05.01.2012 12:20:29)
- Klassen und abstrakte Datentypen (16.01.2012 13:13:57)
- Java (26.01.2012 14:23:48)
- Scheme (11.02.2012 14:53:43) (user.ss)
Hinweise zu den Übungen
Übungsaufgaben
Abgabedatum: 7.11. 17:00 UTC
Punktzahl: 20P
- Beantragen Sie ein Zertifikat. Sollten Sie in einer Gruppe arbeiten, muss jeder von Ihnen diesen Schritt durchführen. Die HPI-Registrierung hat für diese Veranstaltung die folgenden zusätzlichen Öffnungszeiten eingerichtet:
- Freitag, 21.10., 12:30-13:30
- Dienstag, 25.10., 12:30-13:30
- Freitag, 28.10., 12:30-13:30
- Dienstag, 1.11., 12:30-13:30
- Freitag, 4.11., 12:30-13:30
- Melden Sie sich am Aufgabenabgabesystem an. Für die Arbeit in einer Gruppe muss ein Mitglied eine Gruppe anlegen und
das andere Mitglied in die Gruppe aufnehmen.
- Geben Sie die im Dezimalsystem dargestellten Zahlen 62, 1000, 1024, 100000 und 1048576
in den folgenden Positionssystemen an:
- Binärsystem
- Oktalsystem
- Hexadezimalsystem
- Positionssystem zur Basis 11
- Geben Sie die im Dezimalsystem dargestellten Zahlen -1, 1, 28, -35, -256, 257 im
Zweierkomplement in Binärdarstellung und Hexadezimaldarstellung an. Verwenden Sie in
der Darstellung jeweils 16 Bit.
- Ermitteln Sie mit Hilfe der
Unicode-Zeichendefinitionen
oder Code-Charts die Unicode-Zeichenpositionen
für
- der Buchstabe Æ
- das Pfund-Zeichen (£)
- der griechische Buchstabe rho (ρ)
- das Kleiner-oder-gleich-Zeichen (≤)
- Schreiben Sie Ihre Lösungen in eine (reine) Textdatei, und geben Sie
diese beim Abgabesystem ab.
2. Aufgabe
Abgabedatum: 21.11. 17:00 GMT
Punktzahl: 20P
- Machen Sie sich mit /usr/bin/man vertraut. Rufen Sie dazu 'man man' auf.
- Machen Sie sich mit /bin/tar vertraut. Lesen Sie dazu tar(1).
- Entwickeln Sie ein Programm teiler.py, das für eine natürliche Zahl
N alle Teiler ausgibt, die kleiner als N sind. N soll interaktiv durch
den Nutzer eingegeben werden; das Programm soll sich nach Ausgabe aller
Teiler beenden. Die Berechnungen dürfen alle vordefinierten Operatoren
in Python verwenden, jedoch keine Funktionen (mit Ausnahme von
input();
in Python 3 sind auch int() und print() erlaubt).
Die Teiler sollen auf die Standardausgabe ausgegeben werden, mit einer
Zahl pro Zeile im Dezimalsystem. Diese Einschränkungen
(Eingabe durch input(), keine weiteren Funktionen, Ausgabe dezimal
auf die Standardausgabe) gelten auch für die Teile d) und e)
- Entwickeln Sie ein Programm fakultaet.py, das für eine natürliche
Zahl seine Fakultät ausrechnet (n! = n * (n-1) * (n-2) * ... * 2 * 1)
und ausgibt.
- Kombinieren Sie teiler.py und fakultaet.py zu zerlegung.py;
dieses Programm soll die Primzahlzerlegung der Fakultät einer vom
Nutzer abgefragten Zahl ausgeben. Beachten Sie, dass in der
Primzahlzerlegung ein Primfaktor u.U. mehrfach enthalten ist.
Die Ausgabe sollte von der Art
5 ! = 1 * 2 * 2 * 2 * 3 * 5
sein.
- Geben Sie eine Tar-Datei ab, die in einem Verzeichnis aufgabe2
die drei Programme enthält.
3. Aufgabe
Abgabedatum: 5.12., 17:00 UTC
Punktzahl: 20P
Alle Programme in dieser Aufgabe sollen in Python formuliert werden;
unter beliebiger Verwendung der
Standardbibliothek.
Zur Umwandlung von Strings in Zahlen können die Funktion
int und
float verwendet werden.
- Gegeben seien die Terminalsymbole a, b und c, sowie die Hilfssymbole X, Y und Z.
Welche Symbolfolgen können aus diesen Regeln erzeugt werden, wenn man
jeweils X, Y oder Z als Startsymbol wählt? Geben Sie für jedes Hilfssymbol
5 Beispiele an.
- X ::= (a b a) *
- Y ::= c | a Y b | b Y a
- Z ::= [X] [Y]
- Formulieren Sie eine Funktion sort, die drei als Parameter übergebene
Zahlen aufsteigend sortiert und als in einem Drei-Tupel in aufsteigender Reihenfolge
zurückgibt; die Verwendung eines Sortieralgorithmus der Standardbibliothek
ist hier nicht erlaubt.
- Formulieren Sie ein Programm round.py, welches eine auf der Kommandozeile
gegebene Zahl auf das nächste Vielfache von 100 rundet. Dabei soll
"unverzerrt" gerundet werden, sind die letzten zwei Stellen also 50, soll
auf das nächste gerade Vielfache von 100 gerundet werden. Beispiele:
613 wird auf 600 gerundet, 1072 auf 1100, 1150 und 1250 beide auf 1200.
- Formulieren Sie zwei Programme, die die Summe der ersten N
natürlichen Zahlen ausgeben; N sei als Parameter auf der Kommandozeile
gegeben. Entwickeln Sie zwei Varianten dieses Programms, nämlich
- eine Version iterativ.py, die die Zahlen iterativ addiert und
- eine Version rekursiv.py, die die Zahlen rekursiv addiert.
- Heron von Alexandria wird ein Verfahren zur Approximation von
Quadratwurzeln positiver Zahlen zugeschrieben. Dabei wird die
Wurzel der Zahl a durch die Folge
xn+1 = (xn + a/xn)/2
angenähert.
- Formulieren Sie einen rekursiven Algorithmus in sqrt.py, der xk
für auf der Kommandozeile gegebene Werte von a und k berechnet und
ausgibt. Dabei sei a als positive reelle und k als natürliche
Zahl im Dezimalsystem gegeben. x1 sei 1.
- Testen Sie Ihren Algorithmus für verschiedene Werte von a und k
- Bestimmen Sie einen Wert N von Schritten, für den das Verfahren die
Wurzel mit hinreichender Genauigkeit ermittelt; definieren Sie dazu
eine geeignete Spezifikation für "hinreichende Genauigkeit". Hängt
der Wert von N von der Zahl a ab?
- Die Ackermann-Funktion A(m, n) (Wilhelm Ackermann, 1928)
ist für natürliche Zahlen m und n wie folgt definiert:
- A(0, n) = n+1 für n ≥ 0
- A(m, 0) = A(m-1, 1) für m > 0
- A(m, n) = A(m-1, A(m, n-1)) für m, n >0
Lösen Sie die folgenden Teilaufgaben:
- Entwickeln Sie ein Programm a1.py, das für zwei auf der Kommandozeile
angegebene Zahlen den Wert der Ackermannfunktion ausgibt
- Erweitern Sie das Programm zu a2.py, so dass es nicht nur den Wert der
Funktion bestimmt, sondern auch die Zahl Z der Aufrufe der Funktion
ermittelt und ausgibt.
- Erweitern Sie das Programm zu a3.py, so dass es zusätzlich auch noch
die maximale Rekursionstiefe R berechnet und ausgibt.
- Bestimmen Sie für m=1,2,3,4 die maximalen Werte von n, für
die auf Ihrer Maschine die Ackermann-Funktion noch fehlerlos
berechenbar ist, und geben Sie die Werte von n, A(m,n), Z und R
an.
- Senden Sie Ihre Lösung als Tardatei ein, mit Verzeichnissen
a,b,c,d,e,f für die Teilaufgaben.
4. Aufgabe
Abgabedatum: 19.12. 17:00 UTC
Punktzahl: 20P
Alle Programme in dieser Aufgabe sollen in Python formuliert werden.
Sie dürfen beliebige Funktionen der Standardbibliothek verwenden.
- Gegeben sei eine Liste von Nutzernamen passwd
(Password-Datei, latin-1-kodiert),
die einen Nutzer pro Zeile enthält und die Nutzer in der Form
login_name:password:UID:GID:user_name:directory:shell
enthält. Entwickeln Sie ein Modul account.py,
das die folgenden Typen und Funktionen enthält:
- Account ist eine Datensatz-Klasse mit den Feldern
login_name, password, UID, GID, user_name, directory und shell.
Dabei sollen UID und GID ganze Zahlen sein, alle anderen Felder
Strings (<type 'str'>; Python
3: aus Kompatibilität mit der ursprünglichen Aufgabenstellung
auch <type 'bytes'>).
- read(dateiname) liest eine Passwort-Datei, und gibt eine Liste
von Account-Datensätzen zurück, in der Reihenfolge, wie sie auch
in der Datei stehen.
- find_account(liste, login_name) durchmustert eine solche Liste
und gibt den Datensatz mit dem angegebenen login_name zurück. Falls
kein solcher Datensatz gefunden wurde, soll die Ausnahme KeyError
ausgelöst werden.
- Testen Sie Ihr Modul, indem Sie ein Programm schreiben, welches
die Funktionen account.read und account.find_account aufruft, und
mittels assert-Anweisungen überprüft, dass die Ergebnisse richtig sind.
Setzen Sie dabei wenigstens die folgenden Testfälle um:
- Die UID des ersten Nutzers ist 5316.
- Der Name des letzten Nutzers ist Steffen Kensy.
- Es gibt genau einen Nutzer mit der UID 5131.
- find_account(liste,"fwesack") liefert einen Nutzer, dessen
Verzeichnis /home/stud/2005/fwesack ist.
- find_account(liste,"billg") liefert eine Ausnahme.
- Gegeben sei das Programm expr.py, mit welchem
sich Ausdrücke bestehend aus Zahlen und den Grundrechenarten darstellen
lassen (im Beispiel 3*(4+5)). Erweitern Sie es um zwei Funktionen calc
und infix:
- calc erwartet einen Ausdruck, und liefert den berechneten
Wert zurück (im Beispiel 27).
- infix erwartet einen Ausdruck, und liefert einen String zurück,
der eine Infix-Darstellung des Ausdrucks enthält (im Beispiel
"3*(4+5)"); es ist Ihnen freigestellt, "überflüssige" Klammern
wegzulassen (3+4*5 kann also auch als "3+(4*5)" dargestellt werden).
Hinweis: Zur Erkennung, ob ein Term einen binären Ausdruck
oder eine Zahl darstellt, können Sie die Länge des Tupels überprüfen
(die also 1 oder 3 sein muss).
- Senden Sie Ihre Lösung als Tar-Datei ein, mit Unterverzeichnissen für die Teilaufgaben.
5. Aufgabe
Abgabedatum: 23.1. 17:00 GMT
Punktzahl: 20P
- Richten Sie in Ihrem Subversion-Repository
die Verzeichnisse aufgabe5, aufgabe5/trunk, aufgabe5/tags,
aufgabe5/branches ein. Fügen Sie Ihre Lösungen
der anderen Teilaufgaben in aufgabe5/trunk ein.
- Gegeben sei der folgende Algorithmus zur Berechnung des
Durchschnitts zweier Listen:
def intersection(list1, list2):
result = []
for e1 in list1:
for e2 in list2:
if e1 == e2:
# Element ist in beiden Listen enthalten;
result.append(e1)
return result
Beweisen Sie, dass die Ergebnisliste result nur Elemente enthält,
die sowohl in list1 als auch in list2 vorkommen. Hinweis: Formulieren
Sie den Algorithmus zunächst so um, dass anstelle der for-Schleifen
while-Schleifen verwendet werden.
Als Abgabeformat für diese Teilaufgabe wird reiner Text oder PDF akzeptiert.
- Eine doppelt-verkettete Liste ist eine Datenstruktur, in der jedes Element
einen Verweis auf das Vorgänger-Element und einen Verweis auf das Nachfolger-Element
enthält; zusätzlich enthält jedes Element ein (frei wählbares) Datum.
Diese Datenstruktur lässt sich mithilfe zweier Klassen realisieren:
eine Klasse, die die Liste selbst realisiert, und eine Klasse, die
Elemente der Liste realisiert.
Der Vorteil doppelter Verkettung (gegenüber der einfachen Verkettung)
besteht darin, dass zum Löschen eines Elements nur das Element bekannt
sein muss.
Implementieren Sie ein Modul dlist, in dem eine Klasse
DList
definiert wird, mit folgenden Eigenschaften:
- Der Konstruktor
DList() erzeugt eine Liste ohne
Elemente.
- Die Methode
append(item) erzeugt ein neues Listenelement,
dessen Datum item ist, und hängt diesen Wert an das Ende
der Liste.
- Die Methode
remove(item) löscht ein Element aus der
Liste. Sie löst ValueError aus, falls das Element nicht
in der Liste enthalten ist.
- Die Methode
first() liefert das erste Listenelement
(oder None, falls die Liste leer ist).
Listenelemente unterstützen die Methoden
- previous(): liefert das vorhergehende Listenelement (oder None,
wenn der Anfang der Liste erreicht wurde). (optional)
- next(): liefert das nächste Listenelement (oder None,
wenn das Ende der Liste erreicht wurde).
- data(): liefert den Inhalt des Elements
- Testen Sie Ihre Implementierung anhand des
TestProgramms.
- Überprüfen Sie, dass Ihre Implementierung folgende Eigenschaften
besitzt:
- Das Modul, alle Klassen und alle Methoden besitzen doc strings,
die die Verwendung dieser Konstrukte erläutern
- Die Algorithmen in Ihrem Code enthalten Kommentare, die die
Implementierungsstrategie erläutern.
- Ihr Code enthält keine überflüssigen Tests, Anweisungen,
oder Algorithmen.
- Richten Sie für die Lösung ein Subversion-Tag "abgabe" ein,
und geben Sie im Abgabesystem den vollständigen URL dieses
Tags an.
Debian, Subversion und GNU-TLS: Aktuelle Debian/Ubuntu-Releases
verwenden GNU TLS anstelle von OpenSSL; ersteres ist
fehlerhaft
in der Verwendung von SSL-Klientenzertifikaten. Um das PKCS#12-Datei an
diesen Fehler anzupassen, kann man die folgenden Kommandos ausführen:
- openssl pkcs12 -in hpi.p12 -out hpi.pem -clcerts
- openssl pkcs12 -export -in hpi.pem -out hpi2.p12
6. Aufgabe
Abgabedatum: 7.2. 17:00 UTC
Punktzahl: 20P
- Studieren Sie die
Java-Sprachbeschreibung
und geben Sie Java-Fragmente an, die den Produktionsregeln aus Abschnitt 18.1. für
- CatchClause
- NormalInterfaceDeclaration und
- ForControl unter Verwendung der zweiten Alternative von ForVarControlRest
entsprechen. Erklären Sie jeweils die Bedeutung (Semantik) dieses Konstrukts
in der Sprache Java. Reichen Sie diesen Teil Ihrer Lösung als Text-
oder PDF-Datei ein.
- Lösen Sie die Teilaufgaben 4a in Java. Verwenden Sie dabei die folgende Schnittstelle:
Account soll durch eine Java-Klasse repräsentiert werden, für die
alle Felder public sind und geeignete Datentypen haben.
Account soll eine Methode
public static Account[] open(String filename);
besitzen. Bei Scheitern des Einlesens soll diese Funktion null
zurückgeben.
Hinweise: Zum zeilenweisen Einlesen können Sie die Klasse
BufferedReader
verwenden. Fügen Sie die Strings/Account-Objekte zunächst in einen Container,
etwa Vector
ein, um dann ein Ergebnis-Array der passenden Größe zu produzieren.
- Außerdem soll
Account eine Methode
public static Account find_account(Account[] liste, String login_name);
besitzen, die das zu login_name passende Account-Exemplar liefert, oder
null, falls es kein passendes gibt.
- Lösen Sie die Teilaufgabe 4b in Java. Definieren Sie dazu eine Klasse
AccountTest,
deren Methode main() alle Testfälle ausführt.
- Richten Sie im Subversion-Repository die Struktur
aufgabe6/(trunk,tags,branches)
ein, legen Sie ein Subversion-Tag abgabe an, und senden Sie den Repository-URL
dieses Tags ein.
|