45 votes

Manière pythonique de déterminer si les entrées de liste non nulles sont 'continues'

Je suis à la recherche d'un moyen facile de déterminer si tous les pas Aucun des éléments dans une liste se produire dans un seul continue tranche. Je vais utiliser les nombres entiers, comme des exemples de non Aucun des éléments.

Par exemple, la liste [None, None, 1, 2, 3, None, None] répond à mes exigences de maintien de entier entrées. En revanche, [1, 2, None, None, 3, None] est pas continu, parce qu'il y a Aucun des entrées entre les nombres entiers.

Quelques exemples de plus pour en faire une claire que possible.

Continue:
[1, 2, 3, None, None]
[None, None, 1, 2, 3]
[None, 1, 2, 3, None]

Pas En Continu:
[None, 1, None, 2, None, 3]
[None, None, 1, None, 2, 3]
[1, 2, None, 3, None, None]

Ma première approche a été d'utiliser des variables pour garder une trace de savoir si ou non nous avons eu à venir à travers un None encore, et si oui ou non nous avons dû trouver un int - cela se termine avec un très imbriquées et très difficile de suivre une série de if/else intégré dans une boucle for. (Sur le haut de la laideur, j'avoue que je n'ai pas eu à le faire fonctionner dans tous les cas).

Quelqu'un connais un moyen plus facile de déterminer si la non Aucun des éléments dans une liste se produire dans un seul continue tranche?

44voto

gnibbler Points 103484
 def contiguous(seq):
    seq = iter(seq)
    all(x is None for x in seq)        # Burn through any Nones at the beginning
    any(x is None for x in seq)        # and the first group
    return all(x is None for x in seq) # everthing else (if any) should be None.
 

Voici quelques exemples. Vous pouvez utiliser next(seq) pour obtenir le prochain élément à partir d'un itérateur. Je vais mettre une marque pointant vers le prochain élément après chaque

Exemple 1:

 seq = iter([None, 1, 2, 3, None])        #  [None, 1, 2, 3, None]
                                         # next^
all(x is None for x in seq)            
                                         #        next^
any(x is None for x in seq)            
                                         #                    next^ (off the end)
return all(x is None for x in seq)       # all returns True for the empty sequence
 

exemple2:

 seq = iter([1, 2, None, 3, None, None])  #    [1, 2, None, 3, None, None]
                                         # next^
all(x is None for x in seq)            
                                         #    next^
any(x is None for x in seq)            
                                         #             next^  
return all(x is None for x in seq)       # all returns False when 3 is encountered
 

25voto

DSM Points 71975

Bon 'ol itertools.groupby à la rescousse:

from itertools import groupby

def contiguous(seq):
    return sum(1 for k,g in groupby(seq, lambda x: x is not None) if k) == 1

donne

>>> contiguous([1,2,3,None,None])
True
>>> contiguous([None, 1,2,3,None])
True
>>> contiguous([None, None, 1,2,3])
True
>>> contiguous([None, 1, None, 2,3])
False
>>> contiguous([None, None, 1, None, 2,3])
False
>>> contiguous([None, 1, None, 2, None, 3])
False
>>> contiguous([1, 2, None, 3, None, None])
False

[modifier]

Depuis il semble y avoir une discussion dans les commentaires, je vais vous expliquer pourquoi j'aime cette approche mieux que d'autres.

Nous essayons de savoir si il y a un groupe contigu de non-Aucun des objets, et

sum(1 for k,g in groupby(seq, lambda x: x is not None) if k)

compte le nombre de contiguë non Aucun des objets, à l'aide de la fonction dans la stdlib, qui est conçu pour effectuer la collecte de groupes contigus. Dès que nous voyons groupby, nous pensons que "les groupes contigus", et vice-versa. Dans ce sens, c'est l'auto-documentation. C'est la définition de mon objectif.

À mon humble avis la seule faiblesse est qu'il n'a pas de court-circuit, et qui pourrait être fixe, mais après y avoir réfléchi quelques je préfère encore ce qu'il utilise une primitive j'aime -- "compter le nombre de contiguë non Aucun des groupes", ce que je préfère tout simplement "dites-moi si oui ou non il y a plus d'un contigus non Aucun groupe dès que vous le pouvez".

De nombreuses approches pour mettre en œuvre la dernière compter sur intelligent observations sur le problème, comme "s'il n'y a qu'un groupe contigu de pas-Aucun des objets, alors, si nous balayage jusqu'à ce que nous trouvons le premier pas-Aucun objet, et ensuite analyser les objets jusqu'à ce que nous trouvons le premier non-Aucun groupe si l'un existe, alors si quelque chose ne vous reste Aucun nous donne notre réponse." (Ou quelque chose comme ça, qui est une partie de mon problème: je dois penser à elle.) Pour moi qui se sent comme l'utilisation de "détails de mise en œuvre" sur le problème à résoudre, et se concentre sur les propriétés du problème que nous pouvons utiliser pour le résoudre, plutôt que de simplement en spécifiant le problème de Python et de laisser Python faire le travail.

Je suis un supporter de très peu de cerveau, comme la dit-il, et je voudrais éviter d'avoir à faire le malin, comme dans mon expérience, c'est un parcours jonché d'ÉCHOUER.

Comme toujours, tout le monde est le kilométrage peut varier, bien sûr, et probablement en proportion de leur intelligence.

12voto

Blender Points 114729

Vous pouvez utiliser quelque chose comme itertools.groupby :

 from itertools import groupby

def are_continuous(items):
    saw_group = False

    for group, values in groupby(items, lambda i: i is not None):
        if group:
            if saw_group:
                return False
            else:
                saw_group = True

    return True
 

Cela itérera seulement jusqu'à ce qu'il voit un groupe deux fois. Je ne sais pas si vous envisagez [None, None] , adaptez-le à vos besoins.

7voto

mgilson Points 92954

Ce n'est peut-être pas la meilleure façon de le faire, mais vous pouvez rechercher la première entrée non-None et la dernière entrée non-None , puis vérifier la tranche pour None . par exemple:

 def is_continuous(seq):
    try:
        first_none_pos = next(i for i,x in enumerate(seq) if x is not None)
        #need the or None on the next line to handle the case where the last index is `None`.
        last_none_pos = -next(i for i,x in enumerate(reversed(seq)) if x is not None) or None
    except StopIteration: #list entirely of `Nones`
        return False
    return None not in seq[first_none_pos:last_none_pos]

assert is_continuous([1,2,3,None,None]) == True
assert is_continuous([None, 1,2,3,None]) == True
assert is_continuous([None, None, 1,2,3]) == True
assert is_continuous([None, 1, None, 2,3]) == False
assert is_continuous([None, None, 1, None, 2,3]) == False
assert is_continuous([None, 1, None, 2, None, 3]) == False
assert is_continuous([1, 2, None, 3, None, None]) == False
 

Cela fonctionnera pour tout type de séquence.

7voto

ecatmur Points 64173

La manière naturelle de consommer des éléments de séquence consiste à utiliser dropwhile :

 from itertools import dropwhile
def continuous(seq):
    return all(x is None for x in dropwhile(lambda x: x is not None,
                                            dropwhile(lambda x: x is None, seq)))
 

Nous pouvons exprimer cela sans appels de fonction imbriqués:

 from itertools import dropwhile
def continuous(seq):
    core = dropwhile(lambda x: x is None, seq)
    remainder = dropwhile(lambda x: x is not None, core)
    return all(x is None for x in remainder)
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X