@SuperDAU²: ``f=reduce;`` weil damit das Programm weniger Zeichen braucht. Die `reduce()`-Funktion verwende ich zweimal, das wären also 12 Zeichen. Das Binden an `f` zuzüglich dem Semikolon sind 9 Zeichen und jeweils 1 Zeichen für jeden der beiden Aufrufe macht 11 Zeichen. Damit habe ich also ein ganzes Zeichen eingespart.
Mal an einem sinnfreien Beispiel demonstriert:
Code: Alles auswählen
In [1]: def abcdef():
...: return 42
...:
In [2]: abcdef()+abcdef()
Out[2]: 84
In [3]: f=abcdef;f()+f()
Out[3]: 84
Was ich in der ersten Klammer berechne, ist der erste Schritt von der Haushaltsmischung. Den Ausruck hast Du nicht korrekt zitiert, da fehlt auf beiden Seiten etwas. Das Ergebnis von dem Funktionsaufruf wird nicht `x` zugewiesen, sondern ``r,x``. Die Funtkion gibt ein Tupel mit zwei Werten zurück, wovon der erste Wert an `r` und der zweite an `x` gebunden wird.
Beispiel für so eine Funktion ist `divmod()`, die eine ganzzahlige Division durchführt und sowohl das Ergebnis des Teilens, als auch den Rest zurück gibt. Also 10 durch 3 ist zum Beispiel 3 mit dem Rest 1:
Code: Alles auswählen
In [4]: divmod(10, 3)
Out[4]: (3, 1)
In [5]: a, b = divmod(10, 3)
In [6]: a
Out[6]: 3
In [7]: b
Out[7]: 1
Auf der rechten Seite hast Du die beiden anderen Argumente von `reduce()` bzw. `f()` unterschlagen. Die meisten Editoren unterstützen das Hervorheben von zusammengehörigen Klammerpaaren. Wenn man da den Cursor auf die Klammer nach dem ``f`` setzt, wird die dazugehörende schliessende Klammer (meist farbig) hervorgehoben. Dann siehst Du, dass der komplette Aufruf so aussieht:
Code: Alles auswählen
r,x=f(lambda(a,b),c:([(b>=c,c)]+a,b-(b>=c)*c),[5,10,20,50,100,200,500],([],input()))
Mit ``lambda`` definiert man einfache Funktionen, ohne ihnen einen Namen geben zu müssen. `reduce()` erwartet als erstes Argument nämlich eine Funktion. Und die wird mit ``lambda`` an der Stelle definiert, wo sie auch gleich übergeben wird.
`(a,b)` und `c` sind die Argumente, die von `reduce()` an die Funktion übergeben werden. Und zwar als erstes das Ergebnis vom letzten Aufruf und als zweites ein Element vom "iterable" das `reduce()` als zweites Argument übergeben wurde. Beim ersten Aufruf gibt's ja noch kein Ergebnis vom letzten Aufruf, da wird das dritte Argument von `reduce()` als erstes Argument übergeben.
Mal ein einfaches Beispiel für `reduce()`:
Code: Alles auswählen
In [4]: reduce(lambda a, b: a * b, [3, 5, 10], 1)
Out[4]: 150
Das erste Argument ist eine Funktion, die zwei Argumente entgegennimmt und diese multipliziert. `reduce()` ruft die jetzt für jedes Elemenet in der Liste auf, jeweils mit dem Ergebnis des letzten Aufrufs als erstem Argument und dem jeweiligen Element der Liste als zweitem Argument. Beim ersten Aufruf ist a=1, das dritte Argument von `reduce()`, und b=3, das erste Element der Liste. Ergebnis ist 3. Jetzt wird die Funktion mit a=3 und b=5 aufgerufen => 15, und für die 10 wird die Funktion mit a=15 und b=10 aufgerufen => 150.
Bei mir nimmt die Funtkion als erstes Argument ein Tupel aus Liste mit dem bisherigen Ergebnis und dem Restbetrag entgegen. Ein Aufruf von dem ersten `reduce()` sieht so aus (die 355 habe ich nach dem Aufruf eingegeben, weil das ``input()`` auf eine Eingabe wartet):
Code: Alles auswählen
In [3]: f(lambda(a,b),c:([(b>=c,c)]+a,b-(b>=c)*c),[5,10,20,50,100,200,500],([],input()))
355
Out[3]:
([(False, 500),
(False, 200),
(True, 100),
(True, 50),
(True, 20),
(True, 10),
(True, 5)],
170)
Der Startwert ist ein Tupel aus einer leeren Liste und dem Betrag, der vom Anwender eingegeben wird. In jedem Schritt wird ein Tupel aus Wahrheitswert, ob ein Schein mit dem aktuellen Notenwert (`c`) ausgezahlt werden soll oder nicht und dem Notenwert *vor* die bisherige Liste (`a`) gehängt, und falls nötig der Notenwert vom Restbetrag (`b`) abgezogen. Dabei wird die Besonderheit ausgenutzt, dass sich `True` und `False` bei Rechenoperationen wie 1 und 0 verhalten. Wenn ``(b>=c)`` wahr ist, wird also `1*c` von `b` abgezogen, im anderen Fall `0*c` also letztlich 0.
Der zweite Einsatz von `reduce()` arbeitet im wesentlichen nach dem gleichen Prinzip. Eingabe ist die Liste mit den Flags und Notenwerten aus dem ersten Schritt und einem Tupel aus leerer Liste und Restbetrag als Startwert. Ausgabe ist ein Tupel mit einer Liste von Zeichenketten der Form `'anzahl * notenwert'` und dem Restbetrag, der ignoriert wird, weil nur das erste Element an die `join()`-Methode übergeben wird.
Die beiden Argumente der zweiten ``lambda``-Funktionen sind jeweils zwei Tupel bestehend aus bisheriger Ergebnisliste (`a`) und Restbetrag (`b`), und Wahrheitswert (`c`) ob der Notenwert (`d`) im ersten Durchgang ausbezahlt würde, und dem Notenwert selbt. Das sind ja die Elemente aus der Liste wie man oben im Beispiel sieht.
Ich denke der grösste Knackpunkt ist das Verstehen der `reduce()`-Funktion. Damit tun sich viele Leute ein wenig schwer.
Man muss als Anfänger auch nicht unbedingt sofort "code golf"-Lösungen komplett verstehen. Sich damit auseinander zu setzen fördert vielleicht das Verständnis von den etwas skurrileren Ecken einer Programmiersprache, aber nicht alles was man dabei kennenlernt, gehört in saubere Programme. Ein Beispiel wäre das ``&`` vs. ``and``. Die Bitoperation ist natürlich zwei Zeichen kürzer und funktioniert in vielen Fällen äquivalent zum logischen ``and``, aber verständlicher ist die logische Operation, wenn man die meint und nicht die Bitmanipulation.