77 votes

Boucle "oublie" de supprimer certains éléments

dans ce code, j'essaie de créer une fonction anti-voyelle qui supprimera toutes les voyelles (aeiouAEIOU) d'une chaîne. Je pense que cela devrait fonctionner, mais lorsque je l'exécute, le texte d'exemple "Hey look Words!" est retourné comme "Hy lk Words!" Il "oublie" de supprimer le dernier "o". Comment se peut-il?

 text = "Hey look Words!"

def anti_vowel(text):

    textlist = list(text)

    for char in textlist:
        if char.lower() in 'aeiou':
            textlist.remove(char)

    return "".join(textlist)

print anti_vowel(text)
 

153voto

Henry Keiter Points 8851

Vous êtes à la modification de la liste que vous avez à parcourir, qui est lié à la suite de certains comportements non-intuitifs. Au lieu de cela, faire une copie de la liste afin de ne pas supprimer des éléments à partir de ce que vous êtes à itérer.

for char in textlist[:]: #shallow copy of the list
    # etc

Afin de clarifier le comportement que vous voyez, vérifier cela. Mettre print char, textlist au début de votre (original) en boucle. Vous vous attendez peut-être que ce serait d'imprimer votre chaîne à la verticale, à côté de la liste, mais ce que vous obtenez en fait est-ce:

H ['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
e ['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
  ['H', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!'] # !
l ['H', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
o ['H', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
k ['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!'] # Problem!!
  ['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
W ['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
o ['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!'] 
d ['H', 'y', ' ', 'l', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
s ['H', 'y', ' ', 'l', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
! ['H', 'y', ' ', 'l', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
Hy lk Words!

Alors que ce passe? Le gentil for x in y boucle en Python est vraiment juste sucre syntaxique: il continue d'accéder à la liste des éléments par index. Ainsi, lorsque vous supprimez des éléments de la liste lors de l'itération sur elle, vous commencez à sauter des valeurs (comme vous pouvez le voir ci-dessus). En conséquence, vous ne voyez jamais la deuxième o en "look"; vous l'ignorer parce que l'indice a progressé "passé" lorsque vous avez supprimé l'élément précédent. Puis, quand vous arrivez à l' o en "Words", vous allez supprimer la première occurrence d' 'o', qui est celui que vous avez sauté l'avant.


Comme d'autres l'ont mentionné, les interprétations de la liste sont probablement encore mieux (plus propre, plus claire). Utilisez le fait que Python chaînes sont itératif:

def remove_vowels(text): # function names should start with verbs! :)
    return ''.join(ch for ch in text if ch.lower() not in 'aeiou')

66voto

Martijn Pieters Points 271458

D'autres réponses vous dire pourquoi for saute d'articles que vous le modifiez la liste. Cette réponse vous dit comment vous devez supprimer des caractères dans une chaîne, sans obligation de boucle, à la place.

Utiliser str.translate():

vowels = 'aeiou'
vowels += vowels.upper()
text.translate(None, vowels)

Cette opération supprime tous les caractères figurant dans le deuxième argument.

Démo:

>>> text = "Hey look Words!"
>>> vowels = 'aeiou'
>>> vowels += vowels.upper()
>>> text.translate(None, vowels)
'Hy lk Wrds!'
>>> text = 'The Quick Brown Fox Jumps Over The Lazy Fox'
>>> text.translate(None, vowels)
'Th Qck Brwn Fx Jmps vr Th Lzy Fx'

En Python 3, l' str.translate() méthode (Python 2: unicode.translate()) se distingue en ce qu'il ne faut pas un deletechars paramètre; le premier argument est un dictionnaire de la cartographie de l'Unicode des ordinaux (valeurs entières) à de nouvelles valeurs à la place. Utiliser None pour n'importe quel caractère qui doit être supprimé:

# Python 3 code
vowels = 'aeiou'
vowels += vowels.upper()
vowels_table = dict.fromkeys(map(ord, vowels))
text.translate(vowels_table)

31voto

Ashwini Chaudhary Points 94431

Itérer sur une copie de la liste à l'aide de [:]. Vous êtes à la modification d'une liste lors de l'itération sur elle, cela va se traduire dans certaines lettres d'être raté.

L' for boucle conserve la trace de l'indice, de sorte que lorsque vous supprimez un élément à l'index i, le point suivant à l' i+1th changements de position à l'indice actuel(i) et, donc, dans la prochaine itération, vous allez vraiment vous pick l' i+2e point.

Prenons un exemple simple:

>>> text = "whoops"
>>> textlist = list(text)
>>> textlist
['w', 'h', 'o', 'o', 'p', 's']
for char in textlist:
    if char.lower() in 'aeiou':
        textlist.remove(char)

Itération 1 : Indice = 0.

char = 'W' comme il est à l'index 0. Comme il n'a pas satisfait à cette condition que vous allez faire noter.

Itération 2 : Index = 1.

char = 'h' comme il est à l'indice 1. Plus rien à faire ici.

Itération 3 : Index = 2.

char = 'o' comme il est à l'index 2. Comme cet élément répond à la condition de sorte qu'il sera retiré de la liste et tous les éléments à sa droite se décale d'une position vers la gauche pour combler le déficit.

maintenant, textlist devient :

   0    1    2    3    4
`['w', 'h', 'o', 'p', 's']`

Comme vous pouvez le voir les autres 'o' déménagé à l'indice 2, je.e l'indice actuel va donc être ignorés lors de la prochaine itération. Donc, c'est la raison pour laquelle certains éléments sont à apporter sauté dans votre itération. Chaque fois que vous supprimez un élément de l'élément suivant n'est pas de l'itération.

Itération 4 : Index = 3.

char = 'p' comme il est à l'index 3.

....


Correctif:

Itérer sur une copie de la liste pour résoudre ce problème:

for char in textlist[:]:        #note the [:]
    if char.lower() in 'aeiou':
        textlist.remove(char)

D'autres alternatives:

Compréhension de liste:

Un one-liner à l'aide de str.join et list comprehension:

vowels = 'aeiou'
text = "Hey look Words!"
return "".join([char for char in text if char.lower() not in vowels])

regex:

>>> import re
>>> text = "Hey look Words!"
>>> re.sub('[aeiou]', '', text, flags=re.I)
'Hy lk Wrds!'

16voto

Vous modifiez les données que vous parcourez. Ne fais pas ça.

 ''.join(x for x in textlist in x not in VOWELS)
 

8voto

thefourtheye Points 56958
 text = "Hey look Words!"

print filter(lambda x: x not in "AaEeIiOoUu", text)
 

Sortie

 Hy lk Wrds!
 

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