Frage

Ich frage mich, ob es ein Grund ist, dass es in dem Python kein first(iterable) ist eingebaute Funktionen, ähnlich wie any(iterable) und all(iterable) (es kann irgendwo in einem stdlib Modul gesteckt werden, aber ich sehe es nicht in itertools) . first würde einen Kurzschlussgenerator Auswertung durchzuführen, so dass unnötig (und eine potentiell unendliche Anzahl) Operationen vermieden werden können; d.

def identity(item):
    return item

def first(iterable, predicate=identity):
    for item in iterable:
        if predicate(item):
            return item
    raise ValueError('No satisfactory value found')

So können Sie zum Ausdruck bringen können Dinge wie:

denominators = (2, 3, 4, 5)
lcd = first(i for i in itertools.count(1)
    if all(i % denominators == 0 for denominator in denominators))

Selbstverständlich können Sie nicht tun list(generator)[0] in diesem Fall, da der Generator nicht beendet.

Oder wenn Sie eine Reihe von regulären Ausdrücken haben gegen übereinstimmen (nützlich, wenn sie alle die gleiche groupdict Schnittstelle):

match = first(regex.match(big_text) for regex in regexes)

Sie eine Menge unnötiger Verarbeitung sparen durch list(generator)[0] und Kurzschlüsse auf einer positive Übereinstimmung zu vermeiden.

War es hilfreich?

Lösung

Wenn Sie einen Iterator haben, können Sie einfach seine next Methode aufrufen. So etwas wie:

In [3]: (5*x for x in xrange(2,4)).next()
Out[3]: 10

Andere Tipps

Es gibt ein PyPI Paket „erste“ genannt, das dies tut:

>>> from first import first
>>> first([0, None, False, [], (), 42])
42

Hier ist, wie Sie es verwenden, um die erste ungerade Zahl ist, zum Beispiel zurück:

>> first([2, 14, 7, 41, 53], key=lambda x: x % 2 == 1)
7

Wenn Sie nur das erste Element aus dem Iterator zurückgeben möchten, unabhängig davon, ob wahr oder nicht, dies zu tun:

>>> first([0, None, False, [], (), 42], key=lambda x: True)
0

Es ist ein sehr kleines Paket: es nur diese Funktion enthält, ist es keine Abhängigkeiten hat, und es funktioniert auf Python 2 und 3. Es ist eine einzelne Datei, so dass Sie nicht einmal installieren müssen, um sie zu benutzen.

In der Tat, hier ist fast die gesamte Quellcode (ab Version 2.0.1 von Hynek Schlawack, unter der MIT-Lizenz veröffentlicht):

def first(iterable, default=None, key=None):
    if key is None:
        for el in iterable:
            if el:
                return el
    else:
        for el in iterable:
            if key(el):
                return el
    return default

, fragte ich eine ähnliche Frage vor kurzem (es als Duplikat markiert wurde jetzt diese Frage). Mein Anliegen war auch, dass ich gern würde Einbauten verwenden nur , um das Problem der Suche nach dem ersten wahren Wert eines Generators zu lösen. Meine eigene Lösung wurde dann so aus:

x = next((v for v in (f(x) for x in a) if v), False)

Für das Beispiel des erste regexp Spiel zu finden, dies würde wie folgt aussehen (nicht das erste Übereinstimmungsmuster!):

patterns = [ r'\d+', r'\s+', r'\w+', r'.*' ]
text = 'abc'
firstMatch = next(
  (match for match in
    (re.match(pattern, text) for pattern in patterns)
   if match),
  False)

Es wertet nicht das Prädikat zweimal (wie Sie, wenn nur das Muster würden zu tun haben, wurde zurückgegeben) und es Hacks wie Einheimische nicht in Comprehensions verwenden.

Aber es hat zwei Generatoren verschachtelt, wo die Logik zu verwenden, nur eine diktieren würde. So eine bessere Lösung wäre schön.

Es gibt eine "Scheibe" Iterator in itertools. Es emuliert die Scheibe Operationen, die wir mit in Python vertraut sind. Was Sie suchen ist etwas Ähnliches wie folgt aus:

myList = [0,1,2,3,4,5]
firstValue = myList[:1]

Das Äquivalent mit itertools für Iteratoren:

from itertools import islice
def MyGenFunc():
    for i in range(5):
        yield i

mygen = MyGenFunc()
firstValue = islice(mygen, 0, 1)
print firstValue 

Es gibt einige Unklarheiten in Ihrer Frage. Ihre Definition von ersten und dem Regex Beispiel bedeutet, dass es ein boolean Test. Aber das Nenner Beispiel explizit hat eine if-Klausel; so ist es nur ein Zufall, dass jede ganze Zahl um wahr zu sein passiert.

Es sieht aus wie die Kombination des nächsten und itertools.ifilter geben Ihnen, was Sie wollen.

match = next(itertools.ifilter(None, (regex.match(big_text) for regex in regexes)))

Haskell macht Gebrauch von dem, was Sie gerade beschrieben, wie die Funktion take (oder als Teilfunktion take 1, technisch). Python Kochbuch hat Generator-Wrapper geschrieben, die die gleiche Funktionalität auszuführen wie take, takeWhile und drop in Haskell.

Aber, warum das ist keine eingebaute, Ihre Vermutung ist so gut wie meine.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top