Empfehlung zur Ermittlung der "Länge" eines Iterators

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
DaftWullie
User
Beiträge: 37
Registriert: Donnerstag 17. Mai 2012, 21:28

Hallo,

wenn ich mich auf Spoj.pl an den dortigen Problemen mühe, versuche ich bevorzugt, den Code in Python 3 zu erstellen. Dabei fällt mir des öfteren vor die Füße, dass die neueren Python-Version an vielen Stellen auf iterator setzen (etwa range(), map()), wo bislang Listen verwndet wurden.

Häufig benötige ich in meinem Program aber auch die Länge von Mengen, die durch die iterator gebildet werden. Wenn ich dann eine Typ-Konvertierung in eine Liste erzwinge, habe ich immer das Gefühl, den Vorteil der iteratoren zu "verschenken".

Wie würde man geschickt vorgehen? Hier im Forum habe ich dazu zwar Beiträge gefunden, aber keine, die dazu eine Empfehlung oder einen Trick beinhalten...

Beispielcode:

Code: Alles auswählen

def calc_setsum(numbers):
    numbers = list(numbers)
    mul = sum(numbers)
    setsum = (1<<(len(numbers)-1)) * mul
    return str(setsum % 1000000007)
Gruß
Daft
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Zuerst solltest du den Mengenbegriff genauer benutzen, naemlich fuer tatsaechliche Mengen/Sets und nicht fuer beliebige Container.

Zum Problem: Tus nicht. Wie du schon merkst ist die Laenge fuer ein allgemeines Iterable nicht natuerlich.

Wenn es unbedingt sein muss wuerde ich es so machen:

Code: Alles auswählen

iterable, copy = itertools.tee(iterable)
length = sum(1 for _ in copy)
Aber du solltest dir sicher sein, dass deine Iterables endlich sind ;)
BlackJack

Das mit `itertools.tee()` würde ich so nicht machen. Wenn sowieso schon eine Liste mit allen Elementen erstellt werden muss, kann man das auch direkt machen und dort dann die Länge abfragen statt zum Abfragen der Länge eine 1 für jedes Element in einer Schleife zu addieren *und* intern durch `tee()` eine Liste erstellen zu lassen.

Vielleicht sollte man einfach keinen Code für Python 3 schreiben den man am Ende unter Python 2 ausführen muss. Die Versionen lassen sich parallel installieren, also sehe ich keinen Sinn in dieser Vorgehensweise.
DaftWullie
User
Beiträge: 37
Registriert: Donnerstag 17. Mai 2012, 21:28

Hm, da hab ich mich dann wohl etwas zu ungenau ausgedrückt. Es geht nicht um Code der unter Python 2 und 3 laufen soll. Es soll schon Code für Python 3 sein.

Typischer Anwendungsfall ist ja, dass man als Input (bei SPOJ) eine Reihe von Whitespace-separierten Zahlen in Form eines Strings dargeboten bekommt. Erster Schritt ist dann sowas wie

Code: Alles auswählen

map(int, line.split())
Und schon liegen dann die Zahlen als Iterator vor.
Wenn ich diese Zahlen dann die Summe und die Anzahl benötige, wie im obigen Beispiel, dann verunsichert mich halt, dass der iterator keine Anzahl/ Länge kennt.

Oder sollte ich gleich bei der Konvertierung bei Listen bleiben und sowas wie

Code: Alles auswählen

[int(n) for n in line.split()]
verwenden?

P.S. Das mit den Begriffen Mengen/sets von cofi verstehe ich noch nicht ganz. Bei der Verwendung von map übergebe ich doch ein iterierbares Objekt (In dem Fall die von split() generierte Liste) und erhalte - tja, einen Iterator. Ich bin jetzt nicht so der Meister der Fachsprache und auch kein Informatiker :wink:
BlackJack

@DaftWullie: Wenn Du eine Liste brauchst, dann solltest Du halt einfach eine erstellen. Entweder mit `list()` oder als „list comprehension”.

Zum PS: Da ist halt nirgends eine Menge. Sondern eben eine Iterator. Mengen sind das was der `set`-Datentyp in Python darstellt. Eben Mengen im mathematischen Sinn.
DaftWullie
User
Beiträge: 37
Registriert: Donnerstag 17. Mai 2012, 21:28

Danke für die schnellen Antworten.

Allerdings steh ich immer noch etwas auf dem Schlauch. :K
Ich brauche ja keine Liste ich brauche nur die Summe der Elemente und ihre Anzahl. :wink:

Wenn Ihr mir sagt, es ist der richtige Weg oder der einzige mögliche, aus dem iterator eine Liste zu machen oder eben von vornherein den iterator zu vermeiden, dann ist das ja ok und ich kann meine "Störgefühle" abhaken.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

DaftWullie hat geschrieben: Ich brauche ja keine Liste ich brauche nur die Summe der Elemente und ihre Anzahl. :wink:
Wenn Du die Summe brauchst, musst Du den Iterator ja eh "durchlaufen" - also kannst Du doch ohne schlechtes Gewissen eine Liste daraus machen. Diese kannst Du dann bequem summieren und per ``len`` auch die Länge abgreifen.
DaftWullie hat geschrieben: ... oder eben von vornherein den iterator zu vermeiden...
Wie meinst Du das genau?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@DaftWullie: Wenn Du *unbedingt* mit einem Iterator arbeiten willst:

Code: Alles auswählen

the_sum, length = reduce(lambda a, b: (a[0] + b[0], a[1] + b[1]), zip(map(int, line.split()), repeat(1)), (0, 0))
Mit den entsprechenden Funktionen aus `functools` und `itertools`.
DaftWullie
User
Beiträge: 37
Registriert: Donnerstag 17. Mai 2012, 21:28

Vielen Dank für Eure Hinweise. Ich will übrigens nicht partout mit iterator arbeiten, ich versuche nur zu verstehen, wie man in python 3 bestimmte Aufgaben idiomatisch und effizient löst. Und dabei den Code noch versteht, insofern würde ich Blackjacks letzten Vorschlag eher nicht so oft verwenden, da brauch ich ja immer erstmal 10 Minuten Nachdenkzeit, bevor ich verstehe was passiert.
Hyperion hat geschrieben:
DaftWullie hat geschrieben: ... oder eben von vornherein den iterator zu vermeiden...
Wie meinst Du das genau?
So etwa (ohne map() halt):

Code: Alles auswählen

numbers = [int(n) for n in '1 2 3'.split()]
Antworten