Flatten a sequence

Code-Stücke können hier veröffentlicht werden.
Antworten
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Zur Problemstellung flatten a sequence zu Hauf Lösungen mit Hilfsliste finden kann, hier eine Generator-Lösung:

Code: Alles auswählen

def flatten(xs):
    """
    >>> list(flatten([1, 2, [3, 4], [5, [6, 7]], 8]))
    [1, 2, 3, 4, 5, 6, 7, 8]
    """
    for x in xs:
        try:
            for e in flatten(iter(x)):
                yield e
        except TypeError: # x is not iterable
            yield x
Grüße ... bwbg
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
BlackJack

@bwbg: Probier das mal mit einer Zeichenkette irgendwo als Datum aus. ;-)
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Ja, die Endlosrekursion ;) verrate es nur keinem.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Das Problem bei Zeichenketten ist, das deren Elemente wiederum Zeichenketten sind (welche nur ein Zeichen enthalten) und damit iterierbar sind. Um das Problem mit der Endlosrekursion zu beseitigen, habe ich eine Weiche vorgeschaltet, welche die Länge der Sequenz prüft. Praktisch ist, dass `len` einen auch TypeError wirft, wenn es keine Länge zu ermitteln gibt.

Minimal geänderte Funktion mit neuem test-case:

Code: Alles auswählen

def flatten(xs):
    """
   >>> list(flatten([1, 2, [3, 4], [5, [6, 7]], 8]))
   [1, 2, 3, 4, 5, 6, 7, 8]
   >>> list(flatten([[1, 2], "FooBar", 3, 4]))
   [1, 2, 'F', 'o', 'o', 'B', 'a', 'r', 3, 4]
   """
    for x in xs:
        try:
            if len(x) > 1:
                for e in flatten(iter(x)):
                    yield e
            else:
                yield x[0]
        except TypeError: # x is not iterable OR has no len()
            yield x
Hier stellt sich allerdings die Frage, wie man Zeichenketten behandeln möchte. Derzeit werden diese als Sequenz (von Zeichen) betrachtet. Möglich wäre auch eine Betrachung als "Einheit", jedoch müsste man hier auf den speziellen Typ prüfen, was ich gerne vermeiden möchte.

Grüße ... bwbg
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Und was passiert mit Listen mit 0 Elementen? Ein IndexError.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Code: Alles auswählen

from collections import Iterable

try:
    STRING_TYPES = (str, unicode)
except NameError:
    # Assume Python 3.x
    STRING_TYPES = (str, bytes)


def flatten(obj):
    for item in obj:
        if isinstance(item, Iterable) and not isinstance(item, STRING_TYPES):
            for subitem in flatten(item):
                yield subitem
        else:
            yield item
Zuletzt geändert von snafu am Freitag 25. Juli 2014, 13:16, insgesamt 1-mal geändert.
BlackJack

@snafu: Damit würde man jetzt aber Objekte die zwar iterierbar sind, aber nicht `Iterable`, nicht flachklopfen. Ob ein Objekt tatsächlich (nicht) iterierbar ist, kann man letztendlich nur durch ausprobieren heraus finden. Da gibt es keinen Test den man vorher machen könnte.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@BlackJack: Hast du ein konkretes Beispiel, wo mein Vorab-Test nicht greifen würde?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Bei dem hier zum Beispiel:

Code: Alles auswählen

from collections import Iterable


class It(object):
    def __getitem__(self, index):
        if index < 5:
            return 2*index
        else:
            raise IndexError

if __name__ == "__main__":
    it = It()

    print isinstance(it, Iterable)
    
    for elem in it:
        print elem
Das Leben ist wie ein Tennisball.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Gut zu wissen...
Antworten