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:

Black-Box

Wir benutzen den sogenannen 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.

Black-Box

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.

X

Fehler melden

X

Suche