Station - Der map-Operator
Beispiel: Beitragserhöhung
Im Sportverein steht die Berechnung des jährlichen Mitgliedsbeitrags an. Zwei Beitragberechnungsmodelle sollen durchgespielt werden. Im ersten Modell ist der Beitrag direkt an das Alter (am Ende des Kalenderjahres) gekoppelt. Im zweiten Modell ist der Beitrag nur grob gestaffelt: Kinder, Jugendliche und Erwachsene zahlen jeweils einen festen Betrag.
Eine erstes funktionales Programm
Ein erstes Programm benutzt die folgenden Funktionen, um die beiden Berechnungsmodelle durchzuspielen.
def beitraegeBerechnen1(personenListe):
if personenListe == []:
return personenListe
else:
(vorname, name, geburtsdatum) = personenListe[0]
aktuellesJahr = 2010
alter = aktuellesJahr - geburtsdatum[2]
beitrag = 3 * alter
personNeu = (vorname, name, geburtsdatum, beitrag)
return [personNeu] + beitraegeBerechnen1(personenListe[1:])
def beitraegeBerechnen2(personenListe):
if personenListe == []:
return personenListe
else:
(vorname, name, geburtsdatum) = personenListe[0]
aktuellesJahr = 2010
alter = aktuellesJahr - geburtsdatum[2]
if alter < 10:
beitrag = 20
elif alter < 20:
beitrag = 40
else:
beitrag = 60
personNeu = (vorname, name, geburtsdatum, beitrag)
return [personNeu] + beitraegeBerechnen2(personenListe[1:])
# Test
personendaten = [
('Jennifer', 'Abel', (15,12,1993)),
('Philipp', 'Bach', (21,4,1997)),
('Andreas', 'Beyer', (16,3,1988)),
('Alexander', 'Heinrich', (17,3,1999)),
('Tobias', 'Klein', (1,3,1997)),
('Britta', 'Koch', (23,5,1995)),
('Maximilian', 'Meyer', (21,6,1986)),
('Adriana', 'Müller', (21,1,2001)),
('Silke', 'Schmidt', (13,7,1990)),
('Oliver', 'Schmitt', (14,4,1994)),
('Simone', 'Schuster', (20,12,2000)),
('Pia', 'Schwarz', (11,11,2003)),
('Paul', 'Schwarz', (11,11,2003))]
# Berechnungsmodell 1
print(beitraegeBerechnen1(personendaten))
# Berechnungsmodell 2
print(beitraegeBerechnen2(personendaten))
Aufgabe 1
(a) Teste erst einmal die beiden Funktionen. Entwickle selbst ein weiteres Berechnungsmodell und die passende Berechnungsfunktion.
(b) Vergleiche die Funktionsdefinitionen. Welche Gemeinsamkeiten fallen auf?
(c) Siehst du eine Möglichkeit, eine Funktion höherer Ordnung zu benutzen, um die gemeinsame Berechnungsstruktur zu erfassen?
Der map-Operator
Die beiden Funktionsdefinition oben benutzen das gleiche Schema, um die Personendaten um Mitgliedsbeiträge zu erweitern: Für jede Person wird das Datentupel um eine Komponente erweitert, die nach einer - in beiden Fällen unterschiedlichen Berechnungsvorschrift - ermittelt werden.
Man kann das Schema noch weiter abstrahieren: In beiden Fällen wird innerhalb einer Liste jedes Element mit der gleichen Vorschrift verarbeitet und durch das verarbeitete Element ersetzt. Wenn man die Verarbeitung der Listenelemente mit einer Funktion erfasst, erhält man die folgende Verarbeitungssituation:
Wir benutzen den sogenannten map-Operator zur Erfassung dieser Verarbeitungssituation.
Den Bezeichner myMap
wählen wir, da es map
bereits als vordefinierten
Bezeichner in Python gibt.
Wenn man jetzt die Funktionen beitragBerechnen1
und beitragBerechnen2
definiert, um Beiträge von Personen nach den jeweiligen Berechnungsmodellen zu berechnen,
so kann man den map-Operator zur Lösung des oben beschriebenen Problems einsetzen.
Die Funktion myMap
ist eine Funktion höherer Ordnung. Eine Implementierung (und Verwendung)
könnte so aussehen:
def myMap(liste, f):
if liste == []:
return liste
else:
return [f(liste[0])] + myMap(liste[1:], f)
# Test
personendaten = [
('Jennifer', 'Abel', (15,12,1993)),
('Philipp', 'Bach', (21,4,1997)),
('Andreas', 'Beyer', (16,3,1988)),
('Alexander', 'Heinrich', (17,3,1999)),
('Tobias', 'Klein', (1,3,1997)),
('Britta', 'Koch', (23,5,1995)),
('Maximilian', 'Meyer', (21,6,1986)),
('Adriana', 'Müller', (21,1,2001)),
('Silke', 'Schmidt', (13,7,1990)),
('Oliver', 'Schmitt', (14,4,1994)),
('Simone', 'Schuster', (20,12,2000)),
('Pia', 'Schwarz', (11,11,2003)),
('Paul', 'Schwarz', (11,11,2003))]
def beitragBerechnen1(person):
(vorname, name, geburtsdatum) = person
aktuellesJahr = 2010
alter = aktuellesJahr - geburtsdatum[2]
beitrag = 3 * alter
personNeu = (vorname, name, geburtsdatum, beitrag)
return personNeu
def beitragBerechnen2(person):
(vorname, name, geburtsdatum) = person
aktuellesJahr = 2010
alter = aktuellesJahr - geburtsdatum[2]
if alter < 10:
beitrag = 20
elif alter < 20:
beitrag = 40
else:
beitrag = 60
personNeu = (vorname, name, geburtsdatum, beitrag)
return personNeu
# Berechnungsmodell 1
print(myMap(personendaten, beitragBerechnen1))
# Berechnungsmodell 2
print(myMap(personendaten, beitragBerechnen2))
Welche Vorteile bietet der map-Operator? Er liefert eine Standardlösung für eine sehr allgemeine Problemklasse. Wenn man diese Standardlösung benutzt, muss man das Rad nicht jedesmal neu erfinden. Man kann verschiedene Teilprobleme entkoppeln, verringert Fehlerquellen, vermeidet u.U. Codeduplizierung, ...
Noch eine Python-Implementierung des map-Operator
In Python kann die Funktion myMap
auch ganz kurz so implementiert werden:
def myMap(liste, f):
return [f(x) for x in liste]
Diese Implementierung nutzt eine sogenanne List-Comprehension. Das ist eine Anweisung, mit der man in Python eine Liste elegant erzeugen kann.