Die Stadtbücherei
AFB I–IIEine Stadtbücherei zählt mit einem kleinen Programm, wie viele Medien eine Familie gerade ausgeliehen hat und wie viel Säumnisgebühr insgesamt angefallen ist. Pro Medium und Tag Verspätung werden 10 Cent fällig.
public class Ausleihe { int ausgeliehen = 0; int gebuehren = 0; // in Cent void ausleihen(int anzahl) { ausgeliehen = ausgeliehen + anzahl; } int zurueckgeben(int anzahl, int tageZuSpaet) { int gebuehr = anzahl * tageZuSpaet * 10; ausgeliehen = ausgeliehen - anzahl; gebuehren = gebuehren + gebuehr; return gebuehr; } }
- Nennen Sie alle Variablen der Klasse und geben Sie jeweils an, ob es sich um ein Attribut (global), eine lokale Variable oder einen Parameter handelt.
- Ermitteln Sie die Werte der Attribute und die Rückgabewerte nach der Aufruffolge
ausleihen(4),zurueckgeben(2, 3),ausleihen(1),zurueckgeben(3, 0). - Beschreiben Sie Gültigkeitsbereich und Lebensdauer der Variable
gebuehr. Warum wird die AnweisungSystem.out.println(gebuehr);in der Operationausleihennicht übersetzt?
Hinweise
Hinweis zu Aufgabe a)
Hinweis zu Aufgabe b)
Hinweis zu Aufgabe c)
gebuehr, wann verschwindet sie — und in welchem Block ist sie deklariert?Erwartungshorizont
Erwartungshorizont zu Aufgabe a)
Attribute (global): ausgeliehen, gebuehren. Parameter: anzahl (in ausleihen), anzahl und tageZuSpaet (in zurueckgeben). Lokale Variable: gebuehr. Die beiden Parameter anzahl sind trotz gleichen Namens verschiedene Variablen.
Erwartungshorizont zu Aufgabe b)
| Aufruf | ausgeliehen | gebuehren | Rückgabe |
|---|---|---|---|
| Start | 0 | 0 | |
| ausleihen(4) | 4 | — | |
| zurueckgeben(2, 3) | 2 | 60 | 60 |
| ausleihen(1) | 3 | — | |
| zurueckgeben(3, 0) | 0 | 60 | 0 |
Am Ende: ausgeliehen = 0, gebuehren = 60. zurueckgeben(2, 3) liefert 2 · 3 · 10 = 60, zurueckgeben(3, 0) liefert 0; ausleihen liefert als void-Operation nichts.
Erwartungshorizont zu Aufgabe c)
gebuehr ist eine lokale Variable von zurueckgeben. Ihr Gültigkeitsbereich reicht von ihrer Deklaration bis zum Ende des Rumpfs dieser Operation. Sie entsteht bei jedem Aufruf von zurueckgeben neu und verschwindet, wenn die Operation endet. In ausleihen ist der Name gebuehr daher unbekannt — der Compiler meldet „cannot find symbol“. Außerdem gäbe es zu dem Zeitpunkt gar keinen Wert: Der letzte Wert existiert nach dem Ende des Aufrufs nicht mehr. Wer die Gebühr später braucht, nutzt den Rückgabewert oder das Attribut gebuehren.
Der Kiosk
AFB II–IIIEin Schulkiosk verwaltet seinen Vorrat an belegten Brötchen mit einem Python-Programm. Ein Verkauf gelingt nur, wenn genug Brötchen da sind. Nachbestellte Brötchen sollen den Vorrat erhöhen.
vorrat = 12 def verkaufe(n): global vorrat if n <= vorrat: vorrat = vorrat - n return True return False def nachbestellen(menge): vorrat = vorrat + menge
- Widerlegen Sie die Aussage: „Ob der Aufruf
verkaufe(8)gelingt, hängt nur vom Argument 8 ab.“ Betrachten Sie dazu die Aufruffolgenverkaufe(8),verkaufe(5)undverkaufe(5),verkaufe(8), jeweils beginnend mitvorrat = 12. - Zeigen Sie, dass der Aufruf
nachbestellen(10)zu einem Laufzeitfehler führt, und korrigieren Sie die Funktion. - Entwerfen Sie eine Fassung von
verkaufeundnachbestellen, die ohne globale Variable auskommt, und zeigen Sie deren Aufruf im Hauptprogramm. Gehen Sie auf Vor- und Nachteile Ihres Entwurfs ein.
Hinweise
Hinweis zu Aufgabe a)
vorrat und Rückgabe.Hinweis zu Aufgabe b)
vorrat in nachbestellen lokal oder global ist? Was wird rechts vom = gelesen?Hinweis zu Aufgabe c)
return heraus. Wo muss dann der Vorrat gespeichert werden?Erwartungshorizont
Erwartungshorizont zu Aufgabe a)
Reihenfolge 1: verkaufe(8): 8 ≤ 12, vorrat = 4, Rückgabe True. verkaufe(5): 5 ≤ 4 ist falsch, Rückgabe False, vorrat bleibt 4.
Reihenfolge 2: verkaufe(5): vorrat = 7, Rückgabe True. verkaufe(8): 8 ≤ 7 ist falsch, Rückgabe False.
Derselbe Aufruf verkaufe(8) liefert also einmal True und einmal False — die Aussage ist widerlegt. Ursache ist der Seiteneffekt: Das Ergebnis hängt vom globalen Zustand vorrat und damit von der Aufrufreihenfolge ab (Endvorrat 4 bzw. 7).
Erwartungshorizont zu Aufgabe b)
In nachbestellen wird vorrat ein Wert zugewiesen, ohne dass global vorrat steht. Nach der Python-Regel ist vorrat damit in der ganzen Funktion eine lokale Variable. Auf der rechten Seite von vorrat = vorrat + menge wird diese lokale Variable gelesen, bevor sie einen Wert hat. Python bricht mit UnboundLocalError ab; die globale Variable bleibt 12. Korrektur:
def nachbestellen(menge): global vorrat vorrat = vorrat + menge
Erwartungshorizont zu Aufgabe c)
def verkaufe(vorrat, n): if n <= vorrat: return vorrat - n return vorrat def nachbestellen(vorrat, menge): return vorrat + menge # Hauptprogramm bestand = 12 if 8 <= bestand: print("Verkauf möglich") bestand = verkaufe(bestand, 8) # 4 bestand = nachbestellen(bestand, 10) # 14
Beide Funktionen erhalten den Vorrat als Parameter und geben den neuen Vorrat zurück; der Bestand wird nur im Hauptprogramm gespeichert. Vorteile: keine Seiteneffekte, das Ergebnis hängt nur von den Argumenten ab, die Funktionen sind einzeln testbar und für mehrere Kioske wiederverwendbar. Nachteile: Der Aufrufer muss den Rückgabewert jedes Mal selbst speichern — vergisst er bestand = …, geht die Änderung verloren. Die Information, ob der Verkauf gelungen ist, muss er selbst prüfen (Vergleich vor dem Aufruf oder alter mit neuem Bestand).
