Higher-Order-Funktionen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
PythonNeuling8
User
Beiträge: 5
Registriert: Sonntag 5. Februar 2023, 15:08

Hallo, ich würde gerne eure Meinung hören zu folgenden Programm, also ob man sie noch etwas kompakter formulieren kann, aber ohne importierte Module, beziehungsweise ob sie im Großen und Ganzen die jeweilige Aufgabenstellung gut erfüllen (Ich bin frischer Anfänger). Bei der Lösung musste ich mich auf elementare Programmiermethoden beschränken, also auf gewöhnliche Schleifen etc. :)

1. a) Man definiere eine Funktion, welche eine gegebene Liste filtert - und zwar unter Verwendung einer übergebenen Funktion, die für jedes Element True oder False zurückgibt. Man soll also die schon eingebaute filter-Funktion selber implementieren. Beispiel:

Code: Alles auswählen

res_a = filter_mut(list(range(12)), lambda x: x > 3)
# Ausgabe [4, 5, 6, 7, 8, 9, 10, 11]
Meine Lösung einmal als mutierende Funktion und einmal nicht-mutierend:

Code: Alles auswählen

# definieren eine nicht-mutierende Funktion
def filter(liste, function):
    # erzeugen leere Liste, in denen Elemente vorkommen werden, für die das Prädikat True ergibt 
    l = []
    # jedes Element mit True als Rückgabewert wird hinzugefügt
    for element in liste:
        if function(element) == True:
            l.append(element)
        else:
            pass
    return l     

# mutierende Funktion
def filter_mut(liste, function):
    # legen index i fest
    i = len(liste)
    # prüfe für jedes Element (vom letzten zum ersten), ob Funktion False liefert 
    while i >= 1:
        if function(liste[i-1]) == False:
            # entferne Element, wenn False Rückgabewert ist
            liste.remove(liste[i-1])
        else:
            pass
        i -= 1
    return liste

# Testprogramm
res1 = filter(list(range(10)), lambda x: x > 5)
res2 = filter(list(range(10)), lambda x: x%2 == 0)
print(res1)  # Ausgabe [6, 7, 8, 9]
print(res2)  # Ausgabe [0, 2, 4, 6, 8]

print() 

res_a = filter_mut(list(range(10)), lambda x: x > 5)
res_b = filter_mut(list(range(10)), lambda x: x%2 == 0)
print(res_a)  # Ausgabe [6, 7, 8, 9]
print(res_b)  # Ausgabe [0, 2, 4, 6, 8]



Analog dazu Aufgabe b): Man definiere eine Funktion zip, welche zwei Listen als Eingabe erhält und diese elementweise mit einer übergebenen zweistelligen Funktion kombiniert. Die Ergebnisse der elementweisen Kombination sollen als neue Liste zurückgegeben werden. Man soll also die eingebaute zip-Funktion selbst implementieren.

Meine Lösung:

Code: Alles auswählen

def listToDict(a, k):
    # Erstellen von Dictionary und Liste, die die Elemente der k-ten Spalte enthält
    dc = {}                      
    l = []
            
    # Entfernen der k-ten Spalte der Tabelle und Hinzufügen dieser in eine separate Liste,
    # die damit die Schlüssel des Dictionarys enthält
    for i in range(len(a)):
        l.append(a[i][k])
        a[i].remove(a[i][k])
    # jedem Schlüssel, also i-tem Element der sep. Liste, die restliche i-te Liste aus a zuordnen
    for i in range(len(l)):
        dc[l[i]] = a[i]
        
    return dc

# Testprogramm
a = [["Mustermann", "Max", 1234],["Exemplaris", "Emilie", 7890]]
b = [["Mustermann", "Max", 1234, 'Berlin'],["Exemplaris", "Emilie", 7890, 'Kiel'],
     ["Richter", "Horst", 4444, 'München']]

d = listToDict(a, 2)
print(d)

d2 = listToDict(b,1)
print(d2)
Benutzeravatar
__blackjack__
User
Beiträge: 14065
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@PythonNeuling8: Die Funktionen die ein Argument verändern geben das nicht auch noch mal zurück. Das macht keinen Sinn, ist verwirrend, und damit eine schlechte Schnittstelle.

Verwende keine kryptischen Abkürzungen in Namen und erst recht nicht für den gesamten Namen. `l` ist kein guter Name, nicht nur weil er nichts aussagt, sondern man kann das auch in vielen Schriftarten nicht so gut von `1` oder `I` unterscheiden.

Wenn man `result` meint, sollte man nicht nur `res` schreiben, bei `mutating` (?) nicht nur `mut`.

Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste. Oder auch gar keine *verschiedenen* Namen. Manchmal auch überhaupt gar keinen Namen — man muss nicht jedes Zwischenergebnis an einen Namen binden.

Warum heisst `liste` so? So ziemlich alles andere ist englisch benannt, was an sich auch keine schlechte Idee ist. Da einzelne Elemente `element` heissen, könnte man `liste` beispielsweise `elements` nennen. `item` und `items` ist auch nicht unüblich.

Man macht keine Vergleiche mit literalen Wahrheitswerten. Bei dem Vergleich kommt doch nur wieder ein Wahrheitswert bei heraus. Entweder der, den man sowieso schon hatte; dann kann man den auch gleich nehmen. Oder das Gegenteil davon; dafür gibt es ``not``.

Ein ``if`` oder ``else``-Zweig in dem nur ein ``pass`` steht macht keinen Sinn, das formuliert man um. Also bei ``else`` einfach weg lassen, weil das keinen Sinn/Effekt hat, und im ``if`` negiert man die Bedingung und verschiebt den Code aus dem ``else`` in das ``if`` und lässt das ``else`` auch dort weg.

Das `filter_mut()` ist mehr oder weniger fehlerhaft weil `remove()` linear vom Anfang der Liste nach dem Wert sucht der entfernt wird und nicht das Element entfernt welches getestet wurde. Das mag scheinbar trotzdem funktionieren, auch wenn es unnötig ineffizient ist, aber es gibt auch Werte die können auf diese Weise gar nicht entfernt werden, weil die sich beim Vergleich bei der Suche ungewöhnlich verhalten:

Code: Alles auswählen

In [194]: math.nan == math.nan
Out[194]: False
Die ``while``-Schleife dort ist eigentlich eine ``for``-Schleife und statt immer ``i-1`` für den Indexzugriff zu berechnen, wäre es geschickter gleich mit einem um 1 niedrigeren Wert zu starten.

Von der Effizienz („big O notation“) her wäre es besser von vorne anzufangen, mit zwei Indexwerten, einer zum lesen und einer zum schreiben, und dann nur zu schreiben und den Schreibindex zu erhöhen wenn das Element in der Liste bleiben soll. Dann am Ende die eventuell überschüssigen Elemente der Listenplätze mit ``del`` löschen. So bekommt man, selbst wenn im letzten Schitt die Liste doch noch mal komplett umkopiert wird hinter den Kulissen, wenigstens kein quadratisches Laufzeitverhalten.

Da die eingebaute `filter()`-Funktion nachprogrammiert werden soll, würde ich deren Argumentreihenfolge beibehalten.

Die Lösung von Aufgabe b) passt so überhaupt nicht zur Aufgabenstellung‽

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Die Funktion verändert das Argument `a` — das ist so unerwartet, dass ich das als Fehler sehen würde.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten