---
jupytext:
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
kernelspec:
  display_name: Python 3 (ipykernel)
  language: python
  name: python3
learning:
  objectives:
    apply: [boucle for, liste, indice]
    understand: [parcours de liste]
  prerequisites:
    apply: [liste, range, affectation, condition]
---

+++ {"tags": ["hide-cell"], "nbgrader": {"grade": false, "grade_id": "cell-70053db4086b7995", "locked": true, "schema_version": 3, "solution": false, "task": false}}

# Parcours de listes

+++ {"nbgrader": {"grade": false, "grade_id": "cell-5816d3eaf4977174", "locked": true, "schema_version": 3, "solution": false, "task": false}}

La boucle `for` permet de {definiendum}`parcourir <parcours de liste> une liste`, en
accédant tour à tour à chacun de ses éléments:

```{code-cell} ipython3
liste = ['a', 'b', 'c', 'd']
```

```{code-cell} ipython3
for element in liste:
    print(element)
```

Chaque élément de `liste` a été affecté tour à tour à `element`, puis le bloc
d'instruction a été exécuté déclenchant son affichage.

Dans l'exemple suivante, la fonction `somme` combine un parcours de liste avec un
accumulateur (la variable `somme`) pour calculer la somme de tous les éléments de la
liste passée en paramètre:

```{code-cell} ipython3
def somme(liste):
    somme = 0
    for i in liste:
        somme += i
    return somme
```

```{code-cell} ipython3
somme([1, 2, 3, 4, 5])
```

```{code-cell} ipython3
somme([0, -1, -2, -3, -4])
```

```{code-cell} ipython3
somme([])
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-a4ed18f4741f6f20", "locked": true, "schema_version": 3, "solution": false, "task": false}}

:::{admonition} Exercice
Définissez les fonctions `maximum` et `minimum` qui renvoient respectivement le maximum
et le minimum d'une liste d'entiers.
:::

:::{admonition} Indication
:class: hint dropdown

Utilisez un accumulateur, initialisé avec le premier élément de la liste, et mis à jour
si nécessaire à chaque tour de boucle pour contenir la plus grande (respectivement
petite) valeur rencontrée jusqu'à là.
:::

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-66598512b67237a9
  locked: false
  schema_version: 3
  solution: true
  task: false
---
def maximum(liste):
    ### BEGIN SOLUTION
    m = liste[0]
    for element in liste:
        if element > m:
            m = element
    return m
    ### END SOLUTION
```

```{code-cell} ipython3
---
nbgrader:
  grade: true
  grade_id: cell-eb8446c29cae70a2
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
# Tests
assert maximum([1, 2, 3, 4, 5]) == 5
assert maximum([-10, -5, -2, -3]) == -2
assert maximum([10]) == 10
```

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-901a7d1323c2da99
  locked: false
  schema_version: 3
  solution: true
  task: false
---
def minimum(liste):
    ### BEGIN SOLUTION
    m = liste[0]
    for element in liste:
        if element < m:
            m = element
    return m
    ### END SOLUTION
```

```{code-cell} ipython3
---
nbgrader:
  grade: true
  grade_id: cell-3a041742e411b15a
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
# Tests pour la fonction minimum
assert minimum([1, 2, 3, 4, 5]) == 1
assert minimum([-10, -5, -2, -3]) == -10
assert minimum([10]) == 10
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-713cd55b23ab9595", "locked": true, "schema_version": 3, "solution": false, "task": false}}

## Parcours d'une liste par ses indices

Dans les parcours de liste ci-dessus, il n'y avait besoin que d'accéder aux valeurs de
ces éléments. Dans d'autre cas, il peut être nécessaire d'accéder non seulement aux
valeurs des éléments, mais aussi à leurs indices: où sont ils positionnés dans la liste.

Pour cela, une première approche est de parcourir les indices de la liste avec la
fonction `range()`, et d'accéder à la valeur de chacun de ses éléments par son indice.

+++

:::{admonition} Rappel : La fonction range()
:class: dropdown

La fonction `range` renvoie une séquence d'entiers. Elle est souvent utilisée dans les
boucles pour répéter une action un certain nombre de fois. Elle peut-être utilisée sous
trois formes :

- `range(fin)` : génère une séquence d'entiers de `0` à `fin` exclu.
- `range(début, fin)` : génère une séquence d'entiers de `début` à `fin` exclu.
- `range(début, fin, pas)` : génère une séquence d'entiers de `début` à `fin` exclu, par
  pas de `pas`.
:::

Considérons par exemple la liste suivante:

```{code-cell} ipython3
liste = [10, 6, 3, 9, 0]
```

La boucle `for` suivante affiche tous les éléments de la liste avec leurs indices:

```{code-cell} ipython3
for i in range(len(liste)): # pour i allant de 0 à 5 exclus (la taille de la liste)
    print("l'élément", liste[i], "est à l'indice", i) # On affiche l'élément liste[i] et l'indice i
```

Le parcours d'une liste par ses indices permet notamment de modifier en place certains
éléments de la liste. Dans l'exemple suivant, toutes les valeurs inférieures à 10 d'une
liste de notes sont remplacées par 10:

```{code-cell} ipython3
notes = [12, 5, 14, 7, 18, 6, 19]

for i in range(len(notes)):
    if notes[i] < 10:
        notes[i] = 10

notes
```

Le parcours d'une liste par ses indices permet aussi de comparer des éléments
consécutifs. L'exemple suivant, part d'une liste de températures moyenne journalières, et
affiche les jours où la température a baissé. Notez que l'indice commence à `1` pour
pouvoir comparer les indices `i` et `i-1`.

```{code-cell} ipython3
temperatures = [20, 22, 21, 23, 25, 24, 26]

for i in range(1, len(temperatures)):
    if temperatures[i] < temperatures[i - 1]:
        print ("La température a baissé de", temperatures[i - 1], "à",  temperatures[i], "au jour", i)
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-0122fa98c34dda12", "locked": true, "schema_version": 3, "solution": false, "task": false}}

:::{admonition} Exercice
Définissez la fonction `dernier_indice_maximum` qui renvoie l'indice du maximum d'une
liste. Si le maximum apparaît plusieurs fois dans la liste, on veut l'indice de sa
dernière occurrence.
:::

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-71b61831783dec52
  locked: false
  schema_version: 3
  solution: true
  task: false
---
def dernier_indice_maximum(liste):
    ### BEGIN SOLUTION
    valeur_max = liste[0]
    indice_max = 0
    for i in range(len(liste)):
        if liste[i] >= valeur_max:
            valeur_max = liste[i]
            indice_max = i
    return indice_max
    ### END SOLUTION
```

```{code-cell} ipython3
---
nbgrader:
  grade: true
  grade_id: cell-8161dbd8e68a1a8a
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
assert dernier_indice_maximum([1, 2, 3, 4, 5]) == 4
assert dernier_indice_maximum([1, 5, 3, 5, 4]) == 3
assert dernier_indice_maximum([7, 7, 7, 7]) == 3
assert dernier_indice_maximum([10]) == 0
```

## Parcours d'une liste avec `enumerate`

La fonction `enumerate` fournit un idiome élégant pour parcourir simultanément les
indices et les valeurs d'une liste. Étant donné une liste (ou plus généralement un
conteneur ou un itérable), elle génère une séquence de paires `(indice, valeur)`:

```{code-cell} ipython3
liste = ['a', 'b', 'c', 'd']
```

```{code-cell} ipython3
list(enumerate(liste))
```

On peut alors parcourir les indices et valeurs d'une liste comme suit:

```{code-cell} ipython3
for indice, valeur in enumerate(liste):
    print("la valeur", valeur, "est à l'indice", indice)
```

:::{admonition} Avantages du parcours de liste avec `enumerate`
:class: hint

Le parcours d'une liste avec `enumerate` évite les accès indexés ainsi que les
raisonnements sur le début ou la fin d'indices. Cela limite le risque d'erreur. Cela
donne aussi de la généralité à l'idiome: il peut être utilisé sur des conteneurs ou
itérables sans accès indexé ou dont l'on ne connaît pas à l'avance la taille.
:::
