Aller au contenu

Parcourir

Il faut connaître les différentes façons de parcourir un objet chaîne:

  • for ... in ...
  • for ... in enumerate(...)
  • for ... in range(....)

Parcourir les caractères

On peut parcourir les caractères d'une chaîne avec for in:

>>> a = "Je suis une chaîne"
>>> for caractere in a:
...     print(caractere)
... 
J
e

s
u
i
s

u
n
e

c
h
a
î
n
e

Avec for caractere in a:, la variable caractere va parcourir un à un (dans l'ordre de gauche à droite) chaque caractère de la chaîne.

La boucle:

for caractere in a:
    print(caractere)

s'exécute donc ainsi:

  • La variable caractere prend la valeur 'J' et le corps de boucle est exécuté (ici il s'agit de l'affichage de la valeur de la variable caractere).
  • Puis la variable caractere prend la valeur 'e' et le corps de boucle est exécuté.
  • ... et ainsi de suite jusqu'à épuisement des caractères de la chaîne.

Parcourir avec indice et caractère

>>> a = "Je suis une chaîne"
>>> for i, c in enumerate(a):
...     print(i, c)
... 
0 J
1 e
2  
3 s
4 u
5 i
6 s
7  
8 u
9 n
10 e
11  
12 c
13 h
14 a
15 î
16 n
17 e

Avec for i, c in enumerate(a):, la variable i va parcourir un à un (dans l'ordre croissant) les indices de la chaîne, tandis que la variable c prendra pour valeur le caractère de la chaîne correspondant à l'indice i.

La boucle:

for i, c in enumerate(a):
    print(i, c)

s'exécute donc ainsi:

  • La variable i prend la valeur 0 et la variable c prend la valeur 'J' et le corps de boucle est exécuté (ici il s'agit de l'affichage de la valeur de ces deux variables).
  • Puis la variable i prend la valeur 1 et la variable c prend la valeur 'e' et le corps de boucle est exécuté.
  • ... et ainsi de suite jusqu'à épuisement des valeurs possibles pour les indices de la chaîne.

Parcourir avec les indices

>>> a = "Je suis une chaîne."
>>> for indice in range(len(a)):
...     print(indice, a[indice])
... 
0 J
1 e
2  
3 s
4 u
5 i
6 s
7  
8 u
9 n
10 e
11  
12 c
13 h
14 a
15 î
16 n
17 e
18 .

Avec for indice in range(len(a)):, la variable indice va parcourir un à un (dans l'ordre croissant) les indices de la chaîne.

La boucle:

for indice in range(len(a)):
    print(indice, a[indice])

s'exécute donc ainsi:

  • La variable indice prend la valeur 0
    et le corps de boucle est exécuté (affichage de la valeur de indice et du caractère a[indice], c'est à dire a[0] qui est le caractère 'J').
  • Puis la variable indice prend la valeur 1
    et le corps de boucle est exécuté (affichage de la valeur de indice et du caractère a[indice], c'est à dire a[1] qui est le caractère 'e').
  • ... et ainsi de suite jusqu'à épuisement des caractères de la chaîne.

Exercices

Exercice 1

Écrire un code possible pour le corps de la fonction suivante:

def compte_e(chaine):
    """
    chaine -- chaine de caractères

    renvoie le nombre de lettres e, E de la chaîne
    """

le principe

Le principe à utiliser ici est encore celui de l'accumulateur déjà utilisé (avec filtrage des caractères satisfaisant notre critère: ici être un 'e' ou un 'E').

On utilise une variable compteur et on "visite" un par un les caractères de la chaîne.

On "accumule" des 1 (par sommation) dans compteur à chaque visite d'un caractère e ou E, ainsi au final compteur = 1 + 1 + 1 + ... + 1, expression comptant autant de 1 qu'il y a de caractères e ou E dans la chaîne.

Après chaque "visite" de caractère, compteur aura pour valeur le nombre de caractères 'e' ou 'E' déjà visités (pour cela, avant toute visite, on doit bien sûr intialisé le compteur à 0).

En fin de visite des caractères, tous les caractères de la chaîne ayant été visités, la variable compteur contiendra le nombre total d'occurrences des lettres de l'ensemble {'e'; 'E'}.

ou

On aura besoin de tester si une variable caractere désigne 'e' ou désigne 'E'. En langage Python, on utilise or:

if caractere == 'e' or caractere == 'E':

Attention.

Surtout ne pas écrire if caractere == 'e' or 'E': , cela n'a aucun sens. Le "ou" logique se place entre deux propositions qui peuvent être vraies ou fausses. Par exemple caractere == 'e' peut être vrai ou faux. Et caractere == 'E' peut être vrai ou faux. Mais écrire if caractere == 'e' or 'E': revient à placer un "ou" entre caractere == 'e' et 'E'. Or 'E' n'est pas vrai ou faux ! C'est un caractère, pas une proposition logique.

Bien entendu, on peut dans le langage courant commettre cet abus, mais le langage Python ne comprendra pas l'omission faite et interprétera ce if caractere == 'e' or 'E': d'une façon différente de ce qui est attendu.

Pseudo-code
fonction compte_e_E(chaine):
    compteur ← 0
    Pour chaque caractère de chaine:
        si le caractère est un e ou un E:
            on ajoute 1 au compteur  
    renvoyer compteur  
Un code python possible
def compte_e(chaine):
    """
    chaine -- chaine de caractères

    renvoie le nombre de lettres e, E de la chaîne
    """
    compteur = 0
    for caractere in chaine:
        if caractere == 'e' or caractere == 'E': 
            compteur += 1
    return compteur

On peut vouloir mettre en évidence le fait qu'on ne fait rien si le caractère lu n'est pas 'e' ou 'E' (certains trouveront le code plus facile à lire ainsi, cela dépend de chacun, à vous de voir):

def compte_e(chaine):
    """
    chaine -- chaine de caractères

    renvoie le nombre de lettres e, E de la chaîne
    """
    compteur = 0
    for caractere in chaine:
        if caractere == 'e' or caractere == 'E': 
            compteur += 1
        else:
            pass # instruction "on ne fait rien!"
    return compteur
Pour compter aussi é, è, ...

On pourrait aussi vouloir compter é, è, ê dans les lettres e. On peut alors utiliser in (plutôt que d'accumuler les or):
fichier ipynb

Note

On rappelle que l'extension .ipynb est celle des fichiers jupyter notebook.

Une version statique html de ce fichier ipynb:

déroulement

Regardons un peu en détail le déroulement sur cet essai:

def compte_e(chaine):
    """
    chaine -- chaine de caractères

    renvoie le nombre de lettres e, E de la chaîne
    """
    compteur = 0
    for caractere in chaine:
        if caractere == 'e' or caractere == 'E': 
            compteur += 1
        else:
            pass # instruction "on ne fait rien!"
    return compteur



a = compte_e('ether')
print(a)

On appelle la fonction (partie droite de l'affectation a = compte_e('ether')):

compte_e('ether')

On a alors la situation suivante:

On exécute ensuite les lignes de code du corps de la fonction.

On exécute la ligne:

compteur = 0

La situation:

On arrive ensuite à la ligne:

for caractere in chaine:

On teste si caractere désigne une lettre 'e' et on incrémente compteur si c'est le cas:

if caractere == 'e' or caractere == 'E': 
    compteur += 1
else:
    pass # instruction "on ne fait rien!"

On passe à la valeur suivante de caractere:

Et on exécute le if: caractere ne désigne pas un 'e', on ne fait rien. Le schéma reste le même.

On passe à la valeur suivante de caractere:

Et on exécute le if: caractere ne désigne pas un 'e', on ne fait rien. Le schéma reste le même.

On passe à la valeur suivante de caractere:

Et on exécute le if: caractere désigne un 'e', on incrémente compteur.

On passe à la valeur suivante de caractere:

Et on exécute le if: caractere ne désigne pas un 'e', on ne fait rien. Le schéma reste le même.

On termine la fonction avec la ligne return compteur et on effectue la partie gauche de l'affectation (ligne 17): on affecte ainsi l'étiquette a (de l'espace global) à l'objet contenant la valeur 2 qui a été calculée.
Rappelons qu'à ce stade, la fonction ayant terminé ses calculs, plus aucune étiquette locale à la fonction n'existe. En particulier l'objet chaîne de caractères, utilisé en argument pour l'appel, n'est plus accessible puisqu'aucune étiquette ne désigne cet objet. Mais le nombre de lettres 'e' de cette chaîne est accessible via le nom a.

La dernière étape est l'exécution de print(a) qui affiche à l'écran la valeur de l'objet désigné par a.

Exercice 2

Écrire un code possible pour le corps de la fonction suivante:

def rang_premier_e(chaine):
    """
    chaine -- chaine de caractères

    renvoie l'indice de la première lettre e trouvée dans chaine. 
    Renvoie -1 si e n'est pas présent.
    """

Principe

On utilise une variable témoin et on "visite" un par un les caractères de la chaîne.

Au départ, on itinialise témoin à la valeur -1, pour signifier qu'avant toute visite de caractère, on n'a pas trouvé la lettre 'e'.

Après chaque "visite" de caractère, on met à jour témoin:

  • si le caractère est un 'e', la variable témoin reçoit la valeur de l'indice de la lettre visitée,
  • sinon témoin reste égal à sa valeur précédente.

Le principe tel qu'on vient de le présenter n'est pas correct: on obtiendrait ainsi l'indice du dernier caractère 'e' rencontré, il ne faut pas modifier à nouveau témoin si l'on rencontre à nouveau la lettre 'e'.

Comment savoir si le 'e' rencontré est le premier rencontré? C'est simple: si témoinne vaut plus -1, c'est qu'on a déjà rencontré un 'e', sinon c'est le premier.

On modifie donc le code d'une visite de caractère comme suit:

Après chaque "visite" de caractère, on met à jour témoin:

  • si le caractère est un 'e' et si témoin vaut -1: la variable témoin reçoit la valeur de l'indice de la lettre visitée,
  • sinon témoin reste égal à sa valeur précédente.

En fin de visite des caractères, tous les caractères de la chaîne ayant été visités, la variable témoin contiendra -1 si on n'a jamais rencontré 'e' et l'indice du premier 'e' rencontré sinon.

Pseudo-code
fonction rang_premier_e(chaine):
    témoin ← -1
    Pour chaque caractère de chaine:
        si le caractère est un 'e' et si témoin == -1:
            témoin ← indice du caractère
        sinon
            on ne fait rien
    renvoyer témoin
Un code python

Un premier code possible:

def rang_premier_e(chaine):
    """
    chaine -- chaine de caractères

    renvoie l'indice de la première lettre e trouvée dans chaine. Renvoie -1 si e n'est pas présent.
    """
    témoin = -1
    for indice, caractere in enumerate(chaine):
        if caractere == 'e' and témoin == -1: 
            témoin = indice
        else:
            pass
    return témoin
Stopper la boucle au premier e

On peut allèger ce code en profitant du fait que la fonction est stoppée dès le premier return rencontré (dès qu'on connaît l'image, on peut stopper le calcul):

def rang_premier_e(chaine):
    """
    chaine -- chaine de caractères

    renvoie l'indice de la première lettre e trouvée dans chaine. Renvoie -1 si e n'est pas présent.
    """
    for indice, caractere in enumerate(chaine):
        if caractere == 'e': return indice
    return -1

Important

La ligne return -1 n'a pas besoin de else: si dans une fonction, on rencontre un return, on sort immédiatement de la fonction puisque l'image a été obtenue.
Ainsi:

  • Soit un e dans la boucle a été rencontré et on sort de la fonction avec return indice, la ligne return -1 n'est donc jamais vue dans ce cas.
  • Soit aucun e n'est rencontré dans le parcours de la chaîne. On finit la boucle sans avoir exécuté return indice. Et dans ce cas, on exécute la ligne qui suit, c'est à dire return -1.

fichier ipynb
Et la version statique html:

Exercice 3

On appelle distance de Hamming entre deux chaînes de caractères A et B de même longueur le nombre d'indices i tels que A[i] \neq B[i].

Exemples.

  • distance('ami' , 'amu') = 1
  • distance('don' , 'bon') = 1
  • distance('zozo' , 'bobo') = 2

Écrire une fonction python prenant en entrée deux chaînes de caractères de même longueur et renvoyant la distance de Hamming entre ces deux chaînes.

Principe

On utilise le principe de l'accumulateur: une variable nombre_de_différences est initialisée à 0, puis on va accumuler dans cette variable le nombre de différences rencontrées.

On parcourt donc les indices de 0 à longueur(chaine)-1 (les deux chaînes données en entrée ayant même longueur) et on ajoute 1 à nombre_de_différences à chaque fois que les caractères de même indice sont différents dans les deux chaînes.

Pseudo-code
fonction distance(chaineA, chaineB):
    nb_diff  ←  0 # contiendra le nombre de différences
    Pour indice allant de 0 à longueur(chaineA)-1:
        si chaineA[indice] est différent de chaineB[indice]:
            ajouter 1 à nb_diff
    renvoyer nb_diff
Un code python
def hamming(chaine, chene):
    """
    chaine -- type string
    chene -- type string

    renvoie la distance de Hamming entre chaine et chene.
    """
    assert len(chaine)==len(chene), "Attention, les deux chaînes de caractères doivent être de même longueur."

    difference = 0 # compteur de différences
    for k in range(len(chaine)):
        if chaine[k] != chene[k]:
            difference = difference + 1
        else:
            pass # on ne fait rien lorsque les deux caractères sont les mêmes
    return difference

Tests:

>>> hamming('abri', 'ubri')
1
>>> hamming('010101', '010110')
2

in

Le in des boucles for peut-être utilisé pour tester la présence ou non d'un caractère dans une chaîne.

>>> 'a' in 'babar'
True
>>> 'a' in 'Python'
False

Exercice

Ecrire une fonction prenant en entrée une chaîne de caractères et renvoyant True si la chaîne contient au moins une voyelle et False si la chaîne n'en contient pas.

principe

On parcourt la chaîne: on peut arrêter dès qu'on rencontre une voyelle (en renvoyant True). Après le parcours complet de la chaîne, si on n'a pas rencontré de voyelle (c'est à dire si la valeur True n'a pas été renvoyée), on renvoie False.

fonction contient_voyelles(chaine):
    Pour chaque caractère c de chaine:
        si c est une voyelle:
            renvoyer True
    renvoyer False

Il vous reste à trouver comment traduire efficacement en Python le test si c est une voyelle.

un code python
def contient_voyelles(chaine):
    """
    chaine: de type str
    Renvoie True si chaine contient une voyelle,
    renvoie False si chaine ne contient pas de voyelle.
    """
    voyelles = "aeiouy"
    for c in chaine:
        if c in voyelles:
            return True
    return False
>>> contient_voyelles("babar")
True
>>> contient_voyelles("trph")
False

On peut également choisir de parcourir l'ensemble des voyelles et stopper dès que l'une d'elles est trouvée dans chaine:

def contient_voyelles(chaine):
    """
    chaine: de type str
    Renvoie True si chaine contient une voyelle,
    renvoie False si chaine ne contient pas de voyelle.
    """
    for v in "aeiouy":
        if v in chaine:
            return True
    return False