Als erstes: Hände weg vom Galileo Open Book!

Gründe dafür wurden hier im Forum zur Genüge diskutiert.
Peak_me hat geschrieben:
Code: Alles auswählen
b=list(chain.from_iterable(starmap(repeat, izip(count(), a))))
Wow, das ist die schnellste Variante!
Ich habe nur keine Idee, was dieser Ausdruck genau tut.
Vielleicht kann mir jemand das strukturiert auseinander nehmen?
Ist eigentlich gar nicht so schwer
Also fangen wir mit dem hinteren Teil an und arbeiten uns nach vorne durch.
Code: Alles auswählen
In [46]: zip(count(), a)
Out[46]: [(0, 0), (1, 3), (2, 2), (3, 0), (4, 3), (5, 5), (6, 2)]
count() ist eine Iterator-Funktion, die einfach bis von 0 bis zum Zahlenüberlauf von Integerwerten zählt. (Was passiert danach eigentlich?) In "a" stehen Deine Häufigkeiten. Mittels zip() werden immer ein Element des ersten Iterables mit dem des zweiten in ein Tupel zusammengefügt. Damit haben wir den Messwert und seine Häufigkeit zusammen generiert. Wie mir jetzt auffällt hätte man sich das auch schenken können und einfach
schreiben können
repeat() ist eine Funktion, die einen Wert beliebig oft wiederholt. Wir wollen nur den Messwert entsprechend seiner Häufigkeit wiederholen.
Code: Alles auswählen
# erstes Pärchen
repeat(0, 0) -> nix
# nächstes Pärchen
repeat(1, 3) -> 1, 1, 1
# noch eines
repeat(2, 2) -> 2, 2
# schnarch...
Diese Funktion manuell aufzurufen ist Mist, also brauchen wir eine Schleife. Statt einer for-Schleife können wir aber auch eine Variante von map() nehmen, nämlich starmap(func, iterable). starmap wendet eine Funktion (1. Parameter) für alle Paare eines Iterables (2. Parameter) an:
Code: Alles auswählen
In [51]: list(starmap(repeat, enumerate(a)))
Out[51]:
[repeat(0, 0),
repeat(1, 3),
repeat(2, 2),
repeat(3, 0),
repeat(4, 3),
repeat(5, 5),
repeat(6, 2)]
Da repeat(1, 3) eben nicht "1, 1, 1" liefert (wie oben der Einfachheit halber noch gezeigt), sondern ein Iterator-Objekt, müssen wir diese gesammelten Iteratoren noch der Reihe nach "abarbeiten".
Code: Alles auswählen
In [63]: r = repeat(1, 3)
In [64]: r.next()
Out[64]: 1
In [65]: r.next()
Out[65]: 1
In [66]: r.next()
Out[66]: 1
In [67]: r.next()
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
Aha! Nach drei mal "1" ist das Ende des Iterators erreicht.
Wir müssen nun also noch die nach und nach eintröpflenden Iteratoren nehmen und einzeln so lange "ausführen", bis deren Ende erreicht ist. Auch das könnte man mittels einer for-Schleife lösen:
Code: Alles auswählen
In [74]: for item in starmap(repeat, enumerate(a)):
....: for element in item:
....: print element,
....:
....:
1 1 1 2 2 4 4 4 5 5 5 5 5 6 6
Das ist auch noch unschön. Aber dafür gibt es in den Itertools chain in einer speziellen Form, nämlich chain.from_iterable(). Dieses nimmt alle Iteratoren, die in einem Iterator enthalten sind (nämlich dem einen, der durch starmap geliefert wird!) und "führt sie aus".
Ich hoffe das war einigermaßen verständlich. Sich mit den itertools und functools auseinanderzusetzen lohnt sich wirklich!