Première · Langages et programmation

Fonctions : paramètres, résultats et portée des variables

Une fonction donne un nom à un traitement pour pouvoir le réutiliser. Sa force vient d’une séparation nette : celui qui l’appelle connaît les données attendues et le résultat promis, tandis que son corps décrit comment ce résultat est construit.

SofienAvec SofienIngénieur et enseignant en informatique
Dans ce chapitre

Un cap pour ce chapitre

Ce que vous saurez faire

  • Expliquer un appel de fonction et son résultat
  • Distinguer paramètres locaux et variables de l’appelant
  • Différencier print et return
Les bases utiles pour commencer

Définir n’est pas exécuter

L’instruction def définit une fonction ; son corps s’exécute lorsqu’un appel est effectué. Dans def double(x), x est un paramètre, c’est-à-dire un nom utilisé à l’intérieur pour recevoir une donnée. Dans double(7), 7 est l’argument fourni. Pour expliquer un appel, commencez par associer les arguments aux paramètres dans leur ordre, puis suivez le corps avec ces valeurs. Une fonction peut être appelée plusieurs fois avec des arguments différents sans recopier son algorithme. Le nom choisi devrait annoncer le service rendu, par exemple distance ou nombre_occurrences.

Pour deux paramètres, l’ordre des arguments compte : une fonction de soustraction appelée avec trois puis huit ne reçoit pas les mêmes données qu’avec huit puis trois. Écrivez une petite table « paramètre / argument reçu » avant de tracer un appel comportant plusieurs données.

Renvoyer permet de poursuivre le calcul

return fournit un résultat à l’endroit de l’appel et termine immédiatement l’exécution de la fonction. print affiche une représentation et sert à communiquer avec l’utilisateur ; il ne remplace pas un résultat utilisable dans une expression. Une fonction qui atteint sa fin sans return renvoie None. On peut donc afficher 14 dans une fonction et pourtant ne pas pouvoir additionner son résultat à 1. Pour construire des programmes composables, les fonctions de calcul renvoient généralement leurs résultats, et l’affichage est organisé par le programme qui les appelle.

Une instruction située après un retour sur le même chemin n’est jamais exécutée. Inversement, un affichage ne termine pas la fonction : les instructions suivantes se poursuivent. Le résultat retourné peut être stocké, comparé ou transmis directement à un nouvel appel.

Chaque appel possède ses noms locaux

Les paramètres et les variables créées dans une fonction sont locaux à son exécution. Dans une fonction qui reçoit x puis exécute x = x + 2, l’affectation change le nom local x. Pour un argument entier, elle ne remplace pas la variable utilisée par l’appelant. Cette distinction évite de compter sur une modification qui n’aura pas lieu. Nous limitons ici les exemples aux nombres ; les objets modifiables demandent ensuite de distinguer réaffectation d’un nom et modification d’un objet partagé.

def augmente(x):
    x = x + 2
    return x

score = 5
nouveau = augmente(score)
print(score, nouveau)

Deux appels successifs ne partagent pas leurs variables locales ordinaires. Le deuxième appel recommence avec ses nouveaux arguments. Pour conserver un résultat entre les appels, le programme extérieur doit le récupérer explicitement puis le retransmettre, au lieu de supposer une mémoire cachée dans le paramètre.

Composer et traiter tous les chemins

Une fonction peut utiliser le résultat d’une autre : double(augmente(5)) appelle d’abord augmente(5), puis transmet son résultat à double. Les parenthèses organisent cette composition. Dans une fonction conditionnelle, il faut vérifier tous les chemins pour respecter le résultat annoncé. Si un return existe seulement dans le cas x > 0, le cas x = 0 peut renvoyer involontairement None. Une spécification simple, quelques cas limites et une trace des appels permettent de repérer cette omission avant qu’elle se manifeste loin de la fonction responsable.

La composition n’est généralement pas commutative. Sur une seule entrée, deux ordres peuvent cependant coïncider : avec zéro, les deux expressions de l’atelier donnent un. Ce cas particulier ne prouve pas leur équivalence pour les autres entiers.

Décomposer un prix en services simples

Une fonction prix_ligne(quantite, prix_unitaire) peut renvoyer leur produit. Une autre total_commande(prix_a, prix_b, livraison) peut additionner trois montants. Pour deux articles à sept euros, trois articles à quatre euros et une livraison de cinq euros, les prix de lignes sont quatorze et douze ; le total est trente et un. Chaque appel a un contrat court et un résultat vérifiable.

La livraison n’est pas ajoutée dans chaque prix de ligne, sinon elle serait facturée deux fois. La décomposition doit suivre le sens des données. Le programme principal peut afficher le total une fois le calcul terminé, ce qui permet aussi de réutiliser les fonctions dans une autre interface.

Tous les chemins doivent tenir la même promesse

Une fonction qui indique le plus grand de deux nombres peut renvoyer le premier si celui-ci est supérieur ou égal au second, puis renvoyer le second dans l’autre cas. L’égalité est ainsi traitée explicitement. Si l’on utilise deux comparaisons strictes et aucun retour supplémentaire, les arguments égaux provoquent un retour implicite de None.

Pour vérifier une fonction conditionnelle, dessinez ses branches puis associez une entrée à chaque chemin : premier plus grand, second plus grand, égalité. Vérifiez aussi le type du résultat. Une branche renvoyant un nombre et une autre un message textuel compliquent les calculs suivants, sauf si cette diversité fait explicitement partie du contrat.

À vous de faire varier les choses

Deux fonctions, deux ordres

Choisissez une entrée et l’ordre de composition. Suivez les résultats intermédiaires sans modifier la valeur de départ.

Lire le résultat de l’expérience initiale

Résultat renvoyé : 16

Le x extérieur reste 3. Chaque appel reçoit son propre paramètre et renvoie une valeur à l’appel suivant.

AppelArgumentRésultat renvoyé
ajoute_un34
carre416

Lire les appels de l’intérieur vers l’extérieur permet de reconstruire la circulation des valeurs.

De la compréhension à l’autonomie

À vous de résoudre

Cherchez d’abord par vous-même. Vérifiez les résultats demandés, utilisez les indices si nécessaire, puis comparez votre méthode à la correction.

Exercice 1 · S’entraîner#

Deux noms semblables

Une fonction calcule x = x + 4 puis renvoie x. Le programme fait x = 3 et y = fonction(x). Donnez x et y à la fin.

Indice 1

Distinguez le nom local x du nom extérieur x.

Indice 2

La fonction reçoit la valeur 3, puis renvoie 7.

Comprendre la correction

Le x extérieur reste 3 et y reçoit 7. L’affectation du corps agit sur le nom local de l’appel. La valeur retournée peut ensuite être affectée à une variable extérieure, mais ce choix appartient à l’appelant. Écrire x = fonction(x) aurait, cette fois, remplacé le x extérieur par 7.

Exercice 2 · S’entraîner#

Afficher ne suffit pas

La fonction double(x) contient seulement print(2 * x). Pourquoi y = double(6) ne donne-t-il pas y = 12 ? Comment corriger pour calculer ensuite y + 1 ?

Indice 1

Observez séparément ce qui apparaît à l’écran et ce qui est renvoyé.

Indice 2

Une fonction sans return explicite renvoie None.

Comprendre la correction

L’écran affiche 12, mais y reçoit None. L’expression y + 1 échoue alors. Il faut remplacer le corps par return 2 * x ; y devient 12 et y + 1 vaut 13. On peut ajouter print(y) à l’extérieur si l’on souhaite aussi montrer le résultat.

Exercice 3 · S’entraîner#

Un chemin sans résultat

Une fonction signe(x) renvoie 1 si x > 0 et -1 si x < 0. Que manque-t-il pour respecter le contrat « renvoyer -1, 0 ou 1 » ?

Indice 1

Examinez une entrée qui ne satisfait aucune des deux conditions.

Indice 2

Zéro est un cas valide, pas une erreur à ignorer.

Comprendre la correction

Il manque le cas x == 0, pour lequel il faut renvoyer 0. Sans ce retour, l’exécution atteint la fin et produit None. On peut utiliser if, elif puis else, chaque branche contenant un return. Le contrat aide ici à reconnaître une omission même sans message d’erreur immédiat.

Exercice 4 · S’entraîner#

Comprendre une composition

On définit ajoute_un(x) qui renvoie x + 1 et carre(x) qui renvoie x * x. Comparez carre(ajoute_un(3)) et ajoute_un(carre(3)).

Indice 1

Calculez d’abord l’appel placé le plus à l’intérieur.

Indice 2

L’ordre des transformations peut modifier le résultat.

Comprendre la correction

carre(ajoute_un(3)) calcule d’abord 4, puis 4 × 4 : le résultat est 16. ajoute_un(carre(3)) calcule d’abord 9, puis ajoute un : le résultat est 10. Les mêmes fonctions sont utilisées, mais leur ordre de composition change le traitement et donc la réponse.

Exercice 5 · Approfondir et relier#

Trois appels pour une commande

Une fonction ligne(q, p) renvoie q * p. Une fonction total(a, b, port) renvoie la somme des trois paramètres. Calculez total(ligne(4, 3), ligne(2, 8), 5) en détaillant les retours. Indiquez où placer l’affichage.

Indice 1

Les appels intérieurs fournissent les arguments du dernier appel.

Indice 2

Le port est ajouté une seule fois.

Comprendre la correction

Les prix de lignes sont douze et seize. Le dernier appel reçoit donc 12, 16 et 5, puis renvoie 33. Un affichage extérieur peut montrer cette valeur sans changer les fonctions de calcul. Si ligne affichait simplement ses produits, les arguments reçus seraient None et l’addition ne fonctionnerait pas.

Exercice 6 · Approfondir et relier#

Un retour avant la fin

Une fonction renvoie x + 1 si x > 0, puis renvoie 0 dans tous les autres cas. Une instruction d’affichage est placée après ce second retour. Donnez les résultats pour -2, 0 et 4, puis expliquez combien de fois l’affichage est atteint.

Indice 1

Chaque retour interrompt l’appel.

Indice 2

Suivez séparément le chemin positif et le chemin non positif.

Comprendre la correction

Pour -2 et zéro, le premier test est faux et le second retour donne zéro. Pour quatre, le premier retour donne cinq et termine déjà l’appel. L’affichage placé après le dernier retour n’est atteint sur aucun chemin. Il faudrait le placer avant le retour concerné, ou afficher le résultat à l’extérieur.

Exercice 7 · Approfondir et relier#

Le compteur qui ne change pas

La fonction suivant(n) renvoie n + 1. Un programme initialise n = 5, appelle deux fois suivant(n) sans affectation, puis calcule r = suivant(suivant(n)). Donnez n et r. Corrigez les deux appels ignorés pour faire augmenter durablement n.

Indice 1

Une valeur retournée peut être ignorée.

Indice 2

Le nom extérieur change seulement si une instruction le réaffecte.

Comprendre la correction

Les deux premiers retours, égaux à six, sont ignorés. Le nom extérieur reste cinq. La composition calcule ensuite six puis sept : r vaut sept. Pour deux augmentations durables, il faut écrire deux fois n = suivant(n). Le premier appel part alors de cinq et le second de six.

Les erreurs qui méritent un détour

Penser que def exécute immédiatement le corps
Repérez les appels effectifs : une fonction définie mais jamais appelée ne réalise pas son traitement.
Utiliser une variable locale après l’appel
Transmettez explicitement l’information utile avec return plutôt que de dépendre d’un nom interne.

La fiche à garder

L’essentiel à retenir

  • Un appel associe arguments et paramètres.
  • return transmet un résultat ; print affiche.
  • Les variables locales appartiennent à l’exécution de la fonction.

Le prochain pas

Retrouver le catalogue de Première

Ce chapitre s’appuie sur le programme officiel de Première (PDF, nouvel onglet). Les explications et exercices sont proposés pour l’apprentissage.