[PYTHON] Itérateur qui peut balayer vers l'avant à partir du milieu de la séquence

introduction

L'itérateur que j'ai écrit plus tôt qui peut balayer vers l'avant à partir du milieu de la séquence

sslice.py


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

Il est assez pratique et utile pour sa courte longueur.

Si tu es toujours

Je veux un itérateur qui puisse être utilisé comme ʻitertools.islice () `pour une séquence, qui soit rapide même s'il scanne à partir du milieu, et qui peut aussi scanner en avant! !! !!

Si vous êtes anxieux, n'hésitez pas à utiliser le code ci-dessus maintenant.

Je ne sais pas pourquoi une telle fonction est préparée ...

Si vous pensez, veuillez voir la suite.

Comment utiliser sslice ()

Tout d'abord, en tant que situation concrète

Je veux extraire de la liste L une liste contenant le nième élément et le maximum d'éléments m avant et après.

Supposons la situation.

Par exemple, si vous avez une liste de certains classements, votre classement est de 12345 et vous souhaitez afficher jusqu'à 5e au-dessus et en dessous de chacun.

Je voulais le rendre un peu plus courant, mais je suis désolé de ne pas en trouver un bon.

Notez que nous voulons garder l'exemple de code simple, donc disons que le premier élément de la liste L est le 0ème élément au lieu du 1er.

Dans ce cas, la 12 345e place est le 12 344e élément.

De plus, il devrait être facile de gérer les rangs inférieurs à vous, nous ne considérerons donc ici que le traitement des rangs supérieurs à vous.

Par conséquent, ce processus est

Depuis la liste L, récupère la liste contenant le plus grand élément m avant le nième élément

C'est devenu une exigence. La méthode à laquelle j'ai pensé à ce stade était d'utiliser le tranchage,

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

C'était un moyen d'obtenir une tranche de la liste avec du code comme.

Bien entendu, il n'y a pas de problème particulier avec cette méthode.

Mais que se passe-t-il si l'exigence de ce processus n'est pas l'élément "maximum m en avant" mais le "maximum" élément "en avant" qui répond à certains critères?

La condition de jugement cette fois est simplement

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

Je dirais. Bref, c'est un nombre impair.

Dans ce cas, il est nécessaire de déterminer si chaque élément remplit les conditions, de sorte qu'au moins le découpage à lui seul ne peut pas résoudre le problème, et un balayage vers l'avant à partir du «n» élément de la liste est nécessaire.

Juste au cas où, une fois que j'ai résumé l'histoire jusqu'à présent,

>>> 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)
...
>>> 

Sous condition

[12343, 12341, 12339, 12337, 12335]

Le but est d'écrire le processus pour obtenir le résultat.

En fin de compte, vous devez retourner la liste des résultats à «[12335, 12337, 12339, 12341, 12343]» afin de les afficher dans l'ordre de classement, mais ignorez ce processus.

Les tranches mangent trop de mémoire

La première chose que j'ai envisagée était de savoir comment combiner les tranches et la notation d'inclusion de liste.

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

Le résultat correct a été affiché.

Cependant, cette méthode ne se moque pas de la consommation de mémoire de la liste générée temporairement lorsque le nombre d'éléments de la liste «L» est grand.

Par conséquent, je rejetterai cette méthode.

ʻIslice () `ne peut pas balayer vers l'avant

La prochaine chose que j'ai envisagée était d'utiliser le module ʻislice () du module ʻitertools que tout le monde aime manipuler avec un itérateur.

>>> 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.
>>>

Pour une raison quelconque, j'ai eu une erreur.

Je me demandais un peu pourquoi ce serait une erreur, mais j'ai finalement compris pourquoi en lisant l'aide de ʻislice () `.

>>> 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.
...

En bref, ʻislice () était censé avoir une méthode itérable, ou iter`, ou un objet séquence comme premier argument.

Les objets liste de Python sont des séquences, mais les objets itérables ne sont pas toujours des séquences, mais souvent pas des séquences, vous ne pouvez donc pas vous attendre à pouvoir scanner dans la direction opposée à partir du milieu.

Ainsi, le step de ʻislice ()` a été conçu pour qu'il ne puisse pas être un nombre négatif.

Je n'abandonne pas, alors

Ne devrait-il pas être possible de spécifier un nombre négatif pour step uniquement lorsque le premier argument de ʻislice ()` est une séquence?

Ce n'est pas surprenant, mais apparemment je ne l'ai pas fait car ce n'est pas beau en Python de changer le comportement en fonction du type d'argument.

Je laisserai aux pythonistes le soin de s'inquiéter davantage de "pourquoi notre Python est si correct et si beau?" Et je reviendrai sur l'histoire de la réalisation de cet objectif.

Créez ʻislice () , sslice () `pour la séquence

J'y ai beaucoup réfléchi, mais à la fin j'ai écrit ʻislice () et sslice () `pour la séquence.

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

Le code ci-dessous a été écrit avec soin de ne pas étendre les tranches en utilisant sslice ().

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

Avec cela, j'ai finalement atteint cet objectif.

Au fait, ʻislice () semble signifier l'itérateur (ʻi) de la tranche de l'objet itérable ( slice), donc si vous nommez l'itérateur de la tranche de la séquencesslice (), c'est un pythoniste dévot. Cela semble être grondé par les gens, mais veuillez comprendre que ʻislice () quand vous voulez des tranches d'itérateur et sslice () `quand vous voulez des tranches de séquence sont très faciles à retenir.

Cas où sslice () est plus rapide que ʻislice () `

sslice () accède à la séquence via des indices, il est donc généralement plus rapide d'utiliser le ʻislice () optimisé de différentes manières si vous pouvez obtenir le même résultat avec ʻislice (). Cependant, il peut être plus rapide d'utiliser sslice () si l'argument start a une valeur élevée.

La raison en est que ʻislice () doit traiter séquentiellement depuis le début, tandis que sslice () `est accessible en indice.

Plus tôt, j'ai écrit que "il devrait être facile de gérer les rangs inférieurs à vous", mais il est préférable d'utiliser sslice () pour cela également.

Il semble qu'il y ait eu plusieurs demandes d'amélioration concernant cette limitation ou spécification de ʻislice () `, mais apparemment mentionnées ci-dessus.

Ce n'est pas beau en Python de changer le comportement en fonction du type d'argument

Il semble qu'il a été rejeté à cause de cela.

Si vous êtes un utilisateur Python bien formé, vous pleurerez et pleurerez dès que vous entendrez l'explication.

Comme prévu Pythonista! Il est facile de se débarrasser des éléments qui privilégient l'exactitude à la facilité d'utilisation. J'aspire à ça!

Je vais peut-être devoir enlever mes vêtements et couvrir l'eau glacée dans le seau de ma tête, mais je suis un peu faible et je ne suis pas bon pour ça, mais laissez-moi passer s'il vous plaît.

Résumé

J'ai écrit un itérateur qui peut balayer vers l'avant à partir du milieu de la séquence.

sslice.py


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

C'est assez pratique et utile pour sa courte longueur, donc si vous voulez un itérateur qui peut être utilisé comme ʻitertools.islice () `pour les séquences, est rapide même si vous scannez à partir du milieu, et pouvez scanner vers l'avant, veuillez l'utiliser S'il te plait donne moi.

C'est si court que j'ai pensé que quelqu'un avait peut-être écrit une fonction similaire dans le passé, mais je viens de trouver le même code à l'exception du nom de la variable, donc je ne revendiquerai pas la licence.

N'hésitez pas à l'utiliser.

(Bonus) Comment utiliser slice.indices ()

Au fait, sslice () utilise slice.indices () car

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

Ceci doit correspondre à des arguments tels que.

Il y a de nombreuses parties de la documentation d'aide Python qui sont si déroutantes que vous pourriez vous demander, "Est-ce que c'est écrit exprès?", Et l'aide pour slice.indices () en fait partie. pense.

>>> 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)

Je ne suis pas le seul à ne pas comprendre à première vue quel était le but de cette méthode après avoir lu l'aide ci-dessus, non? (Transpiration

Un tel slice.indices () est recommandé, mais si vous l'utilisez lorsque vous voulez générer un argument de range () à partir de n'importe quel objet slice, cela sera décidé en une seule fois.

Il n'y a peut-être pas d'autres situations où cette méthode est utile. (Lol)

Recommended Posts

Itérateur qui peut balayer vers l'avant à partir du milieu de la séquence
Obtenir les informations de séquence de la protéine traduite à partir des informations de mutation de CDS
À partir d'un livre que le programmeur peut apprendre ... (Python): trouver la valeur la plus fréquente
À partir d'un livre que les programmeurs peuvent apprendre ... (Python): examen des tableaux
Existence du point de vue de Python
J'ai fait une simple minuterie qui peut être démarrée depuis le terminal
Découvrez le nom de la méthode qui l'a appelée à partir de la méthode qui est python
D'un livre qui apprend de manière intéressante la façon de penser du programmeur (Python)
Ceci et celui de la notation d'inclusion.
Gymnastique algorithmique 24 Milieu de la liste liée
Notes d'apprentissage depuis le début de Python 1
Omettre la nomenclature depuis le début de la chaîne
Obtenez la valeur de la couche intermédiaire de NN
Notes d'apprentissage depuis le début de Python 2
Tensorflow, il semble que même la valeur propre de la matrice puisse être automatiquement différenciée
À partir d'un livre que le programmeur peut apprendre ... Vérification de la somme de contrôle des runes (longueur fixe)