[PYTHON] Iterator, der von der Mitte der Sequenz aus vorwärts scannen kann

Einführung

Der Iterator, den ich zuvor geschrieben habe und der von der Mitte der Sequenz aus vorwärts scannen kann

sslice.py


def sslice(sequence, *slice_args):
    return (sequence[index]
            for index in range(*slice(*slice_args).indices(len(sequence))))

Es ist sehr praktisch und nützlich für seine kurze Länge.

Wenn du immer bist

Ich möchte einen Iterator, der wie "itertools.islice ()" für Sequenzen verwendet werden kann, schnell ist, auch wenn er von der Mitte aus scannt, und auch vorwärts scannen kann! !! !!

Wenn Sie Angst haben, können Sie jetzt den obigen Code verwenden.

Ich habe keine Ahnung, warum eine solche Funktion vorbereitet ist ...

Wenn Sie denken, lesen Sie bitte die Fortsetzung.

Wie man sslice () benutzt

Zunächst als konkrete Situation

Ich möchte aus der Liste "L" eine Liste extrahieren, die das "n" -te Element und die maximalen "m" -Elemente davor und danach enthält.

Nehmen wir die Situation an.

Wenn Sie beispielsweise eine Liste mit Ranglisten haben, beträgt Ihre Rangliste 12.345, und Sie möchten jeweils bis zu 5 darüber und darunter anzeigen.

Ich wollte es etwas häufiger machen, aber es tut mir leid, dass ich kein gutes finden konnte.

Beachten Sie, dass wir den Beispielcode einfach halten möchten. Nehmen wir also an, dass das erste Element in der Liste L das 0. Element anstelle des 1. Elements ist.

In diesem Fall ist der 12.345. Platz das 12.344ste Element.

Darüber hinaus sollte es einfach sein, mit Rängen unter Ihnen umzugehen, sodass wir hier nur die Verarbeitung von Rängen über Ihnen in Betracht ziehen.

Daher ist dieser Prozess

Rufen Sie aus der Liste "L" die Liste mit dem größten "m" -Element vor dem "n" -ten Element ab

Es wurde eine Anforderung. Die Methode, an die ich zu diesem Zeitpunkt dachte, war das Schneiden.

>>> L = list(range(1, 50000+1))
>>> n = 12345 - 1
>>> m = 5
>>> L[max(0, n - m): n]
[12340, 12341, 12342, 12343, 12344]

Es war eine Möglichkeit, einen Teil der Liste mit Code wie zu erhalten.

Natürlich gibt es bei dieser Methode kein besonderes Problem.

Was aber, wenn die Anforderung für diesen Prozess nicht das "maximale" m "-Element voraus ist, sondern das" maximale "m" -Element voraus ", das bestimmte Kriterien erfüllt?

Die Urteilsbedingung ist diesmal einfach

>>> def is_odd(number):
...     return (number % 2 == 1)
...
>>> is_odd(1)
True
>>> is_odd(2)
False

Ich werde sagen. Kurz gesagt, es ist eine ungerade Zahl.

In diesem Fall muss bestimmt werden, ob jedes Element die Bedingung erfüllt, so dass zumindest das Schneiden allein das Problem nicht lösen kann, und ein Vorwärtsscan vom "n" -ten Element der Liste ist erforderlich.

Nur für den Fall, wenn ich die Geschichte bisher zusammengefasst habe,

>>> L = list(range(1, 50000+1))
>>> n = 12345 - 1
>>> m = 5
>>> L[max(0, n - m): n]
>>> def is_odd(number):
...     return (number % 2 == 1)
...
>>> 

Unter der Bedingung

[12343, 12341, 12339, 12337, 12335]

Der Zweck besteht darin, den Prozess zu schreiben, um das Ergebnis zu erhalten.

Am Ende müssen Sie die Ergebnisliste auf "[12335, 12337, 12339, 12341, 12343]" drehen, um sie in der Rangfolge anzuzeigen, aber diesen Vorgang überspringen.

Scheiben verbrauchen zu viel Speicher

Das erste, was ich überlegte, war, wie man Slices und Listeneinschlussnotation kombiniert.

>>> [rank for rank in L[n - 1::-1] if is_odd(rank)][:m]
[12343, 12341, 12339, 12337, 12335]

Das richtige Ergebnis wurde angezeigt.

Diese Methode macht jedoch den Speicherverbrauch der vorübergehend erzeugten Liste nicht zum Narren, wenn die Anzahl der Elemente in der Liste "L" groß ist.

Daher werde ich diese Methode ablehnen.

islice () kann nicht vorwärts scannen

Das nächste, was ich überlegte, war, wie man mit einem Iterator unter Verwendung der "islice ()" des "itertools" -Moduls umgeht, das jeder liebt.

>>> from itertools import islice
>>> islice(L, n - 1, None, -1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Step for islice() must be a positive integer or None.
>>>

Aus irgendeinem Grund habe ich einen Fehler erhalten.

Ich habe mich ein wenig gefragt, warum dies ein Fehler sein würde, aber ich habe schließlich herausgefunden, warum, indem ich die Hilfe für "islice ()" gelesen habe.

>>> help(itertools.islice)
Help on class islice in module itertools:

class islice(builtins.object)
 |  islice(iterable, stop) --> islice object
 |  islice(iterable, start, stop[, step]) --> islice object
 |
 |  Return an iterator whose next() method returns selected values from an
 |  iterable.  If start is specified, will skip all preceding elements;
 |  otherwise, start defaults to zero.  Step defaults to one.  If
 |  specified as another value, step determines how many values are
 |  skipped between successive calls.  Works like a slice() on a list
 |  but returns an iterator.
...

Kurz gesagt, "islice ()" sollte eine iterable oder "iter" -Methode oder ein Sequenzobjekt als erstes Argument haben.

Pythons "Listen" -Objekte sind Sequenzen, aber iterierbare Objekte sind nicht immer Sequenzen, sondern oft keine Sequenzen. Sie können also nicht erwarten, dass Sie von der Mitte aus in die entgegengesetzte Richtung scannen können.

Der "Schritt" von "islice ()" wurde so entworfen, dass es keine negative Zahl sein kann.

Ich gebe also nicht auf

Sollte es nicht möglich sein, eine negative Zahl für "step" nur anzugeben, wenn das erste Argument von "islice ()" eine Sequenz ist?

Es ist nicht überraschend, aber anscheinend habe ich keine solche Spezifikation gemacht, weil es in Python nicht schön ist, das Verhalten abhängig von der Art des Arguments zu ändern.

Ich überlasse es den Pythonisten, mir mehr Gedanken darüber zu machen, warum unser Python so korrekt und schön ist. Und ich werde auf die Geschichte des Erreichens dieses Ziels zurückkommen.

Erstellen Sie "islice ()" und "sslice ()" für die Sequenz

Ich habe viel darüber nachgedacht, aber am Ende habe ich "islice ()" und "sslice ()" für die Sequenz geschrieben.

def sslice(sequence, *slice_args):
    return (sequence[index]
            for index in range(*slice(*slice_args).indices(len(sequence))))

Der folgende Code wurde mit der Sorgfalt geschrieben, Slices nicht mit sslice () zu erweitern.

>>> list(islice((rank for rank in sslice(L, n - 1, None, -1) if is_odd(rank)), 0, m))
[12343, 12341, 12339, 12337, 12335]

Damit habe ich dieses Ziel endlich erreicht.

Übrigens scheint "islice ()" den Iterator ("i") des Slice des iterierbaren Objekts ("Slice") zu bedeuten. Wenn Sie also den Iterator des Slice der Sequenz "sslice ()" nennen, handelt es sich um einen frommen Pythonista. Es scheint von Leuten beschimpft zu werden, aber bitte haben Sie Verständnis dafür, dass es sehr einfach ist, sich an "islice ()" zu erinnern, wenn Sie Iterator-Slices möchten, und an "sslice ()", wenn Sie Sequenz-Slices möchten.

Fall, in dem sslice () schneller ist alsislice ()

sslice () greift über Indizes auf die Sequenz zu, daher ist es normalerweise schneller, die verschiedenen optimiertenislice ()zu verwenden, wenn Sie mit islice () `das gleiche Ergebnis erzielen können. Es kann jedoch schneller sein, "sslice ()" zu verwenden, wenn das Argument "start" einen großen Wert hat.

Der Grund ist, dass "islice ()" von Anfang an sequentiell verarbeitet werden muss, während auf "sslice ()" per Index zugegriffen werden kann.

Früher habe ich geschrieben, dass "es einfach sein sollte, mit den Rängen unter Ihnen umzugehen", aber es ist besser, auch dafür "sslice ()" zu verwenden.

Es scheint, dass es mehrere Verbesserungswünsche bezüglich dieser Einschränkung oder Spezifikation von "islice ()" gegeben hat, die aber anscheinend oben erwähnt wurden.

In Python ist es nicht schön, das Verhalten je nach Art des Arguments zu ändern

Es scheint, dass es aus diesem Grund abgelehnt wurde.

Wenn Sie ein gut ausgebildeter Python-Benutzer sind, werden Sie weinen und weinen, sobald Sie die Erklärung hören.

Wie erwartet Pythonista! Es ist einfach, Dinge loszuwerden, bei denen Korrektheit Vorrang vor Benutzerfreundlichkeit hat. Ich sehne mich danach!

Ich muss mich vielleicht ausziehen und das Eiswasser im Eimer von meinem Kopf abdecken, aber ich bin ein bisschen schwach und ich bin nicht gut darin, aber bitte lass mich passieren.

Zusammenfassung

Ich habe einen Iterator geschrieben, der von der Mitte der Sequenz aus vorwärts scannen kann.

sslice.py


def sslice(sequence, *slice_args):
    return (sequence[index]
            for index in range(*slice(*slice_args).indices(len(sequence))))

Obwohl es kurz ist, ist es sehr praktisch und nützlich. Wenn Sie also einen Iterator möchten, der wie "itertools.islice ()" für Sequenzen verwendet werden kann, schnell ist, auch wenn Sie von der Mitte aus scannen und vorwärts scannen können, verwenden Sie ihn bitte. Bitte gib mir.

Es ist so kurz, dass ich dachte, vielleicht hätte jemand in der Vergangenheit eine ähnliche Funktion geschrieben, aber ich habe nur den gleichen Code gefunden, mit Ausnahme des Variablennamens, sodass ich die Lizenz nicht beanspruchen kann.

Bitte zögern Sie nicht, es zu benutzen.

(Bonus) Wie benutzt man "Slice.indices ()"?

Übrigens verwendet sslice () slice.indices (), weil

>>> sslice(range(20), None, None, -1)

Dies soll Argumenten wie entsprechen.

Es gibt viele Teile der Python-Hilfedokumentation, die so verwirrend sind, dass Sie sich fragen könnten: "Ist das absichtlich geschrieben?", Und die Hilfe für "Slice.indices ()" ist eine davon. Überlegen.

>>> help(slice.indices)
Help on method_descriptor:

indices(...)
    S.indices(len) -> (start, stop, stride)

    Assuming a sequence of length len, calculate the start and stop
    indices, and the stride length of the extended slice described by
    S. Out of bounds indices are clipped in a manner consistent with the
    handling of normal slices.
(END)

Ich bin nicht der einzige, der auf den ersten Blick nicht verstanden hat, was der Zweck dieser Methode war, nachdem er die obige Hilfe gelesen hat, oder? (Schweiß

Ein solches "Slice.indices ()" wird empfohlen, da es auf einmal entschieden wird, ob Sie es verwenden, wenn Sie ein Argument von "range ()" aus einem "Slice" -Objekt generieren möchten.

Vielleicht gibt es keine anderen Situationen, in denen diese Methode nützlich ist. (Lol)

Recommended Posts

Iterator, der von der Mitte der Sequenz aus vorwärts scannen kann
Erhalten Sie die Sequenzinformationen des translatierten Proteins aus den Mutationsinformationen von CDS
Aus einem Buch, das der Programmierer lernen kann ... (Python): Finden Sie den häufigsten Wert
Aus einem Buch, das Programmierer lernen können ... (Python): Überprüfung von Arrays
Existenz aus Sicht von Python
Ich habe einen einfachen Timer erstellt, der vom Terminal aus gestartet werden kann
Den Namen der Methode, die ihn aufgerufen hat, finden Sie in der Python-Methode
Aus einem Buch, das die Denkweise des Programmierers interessanterweise gelernt hat (Python)
Dies und das der Einschlussnotation.
Algorithmus Gymnastik 24 Mitte der verknüpften Liste
Notizen vom Anfang von Python 1 lernen
Lassen Sie die Stückliste am Anfang der Zeichenfolge weg
Ermitteln Sie den Wert der mittleren Schicht von NN
Notizen vom Anfang von Python 2 lernen
Tensorflow scheint es, dass sogar der Eigenwert der Matrix automatisch unterschieden werden kann
Aus einem Buch, das der Programmierer lernen kann ... Überprüfung der Runenprüfsumme (feste Länge)