Langages et paradigmes de programmation

Suis le parcours d’un programme, du code écrit par une personne aux instructions binaires exécutées par un processeur, et découvre comment les compilateurs, les interpréteurs, le bytecode et les compilateurs à la volée assurent cette traduction. Apprends à distinguer la syntaxe d’un langage de sa signification, à comprendre comment les langages vérifient les types de données et où un programme conserve ses données et libère sa mémoire pendant son exécution. Compare ensuite quatre façons d’écrire des programmes (impérative, orientée objet, fonctionnelle et déclarative) en résolvant les mêmes problèmes avec chacune, et apprends à choisir un langage adapté à un projet.

DébutantUnité 5 sur 7 de la formation Formation : Informatique : données, algorithmes, Internet et IA

Ce que tu vas apprendre

  1. Suis le parcours d’un programme, du code écrit par une personne aux instructions binaires exécutées par un processeur, et compare les façons de réaliser cette traduction : à l’avance, pendant l’exécution du programme ou en combinant les deux.
    • Compare le code machine, le langage d’assemblage et les langages de haut niveau
      Décris les trois niveaux auxquels les programmes peuvent être écrits, montre comment une ligne de code de haut niveau correspond à plusieurs instructions machine et explique pourquoi la plupart des logiciels sont écrits au niveau le plus élevé.
    • Explique le rôle d’un compilateur
      Décris comment un compilateur traduit à l’avance un programme entier, nomme ses principales étapes et explique ce qu’il advient des erreurs et de l’exécutable terminé.
    • Explique les interpréteurs, le bytecode et la compilation à la volée
      Décris comment un interpréteur exécute un programme, comment des langages comme Java et Python combinent compilation et interprétation, et comment évaluer les compromis entre ces approches.
  2. Distingue les règles d’écriture du code de celles qui définissent ce qu’il fait, reconnais les éléments communs à la plupart des langages de haut niveau et suis de courts programmes pour prévoir leurs résultats.
    • Distingue la syntaxe de la sémantique
      Explique la différence entre la forme d’un programme et son sens, distingue les erreurs de syntaxe, d’exécution et de logique, et repère les cas où des langages donnent un sens différent à un code d’apparence identique.
    • Suis l’exécution des structures communes aux programmes de haut niveau
      Reconnais les structures présentes dans presque tous les langages de haut niveau (variables, expressions, contrôle du déroulement et fonctions avec leur portée) et suis un court programme étape par étape pour prévoir sa sortie.
  3. Décris comment les langages classent et vérifient les valeurs selon leur type, où un programme conserve ses données pendant son exécution et comment la mémoire est réservée et libérée, par la personne qui programme ou automatiquement, avec les compromis de sûreté et de vitesse associés à chaque choix.
    • Compare la manière dont les langages utilisent et vérifient les types de données
      Explique ce qu’est un type de données, compare le typage statique et dynamique et les règles de conversion strictes et souples, puis prévois comment les langages courants traitent les mélanges de types et les limites des nombres.
    • Décris où un programme conserve ses données pendant son exécution
      Explique que les variables correspondent à des emplacements mémoire nommés, décris les rôles de la pile d’appels et du tas, et distingue la copie d’une valeur du partage d’une référence.
    • Compare la gestion manuelle et la gestion automatique de la mémoire
      Explique comment la mémoire est libérée dans les langages où la personne qui programme la gère et dans ceux qui utilisent le ramassage des miettes ou des règles de possession, et relie les erreurs mémoire aux plantages et aux failles de sécurité.
  4. Décris les programmes comme des séquences de commandes qui modifient l’état enregistré, puis comme des ensembles d’objets qui réunissent données et comportement, et utilise les classes, l’encapsulation, l’héritage et le polymorphisme pour organiser le code.
    • Décris les styles impératif et procédural
      Explique que la programmation impérative utilise des commandes successives qui modifient l’état d’un programme, montre comment les procédures et un contrôle du déroulement structuré organisent ces commandes, et reconnais les risques liés à un état partagé qui change.
    • Modélise des données et des comportements à l’aide de classes et d’objets
      Explique comment la programmation orientée objet regroupe les données et les opérations qui les concernent dans des objets, lis et conçois une classe simple, et utilise l’encapsulation pour protéger les données d’un objet.
    • Réutilise et étends des classes grâce à l’héritage, au polymorphisme et à la composition
      Utilise l’héritage pour construire des classes spécialisées, explique comment le polymorphisme permet à un même appel de se comporter différemment selon les objets, et détermine quand combiner des objets au lieu d’utiliser l’héritage.
  5. Crée des programmes à partir de fonctions qui renvoient de nouvelles valeurs au lieu de modifier les anciennes, et écris des programmes qui indiquent le résultat voulu tandis qu’un moteur décide comment le calculer.
    • Crée des programmes à partir de fonctions pures et de données immuables
      Explique les fonctions pures, l’immuabilité, les fonctions de première classe et d’ordre supérieur et la récursivité, puis réécris une boucle sous forme d’une chaîne de map, filter et reduce.
    • Décris le résultat attendu à l’aide de langages déclaratifs
      Explique que la programmation déclarative consiste à indiquer le résultat voulu plutôt que la manière de l’obtenir, et lis des exemples dans un langage de requête de base de données, un langage logique et d’autres notations déclaratives.
  6. Résous le même problème dans différents styles, reconnais que la plupart des langages modernes mélangent les paradigmes et évalue les facteurs techniques et pratiques qui déterminent si un langage convient à un projet.
    • Résous un même problème selon plusieurs paradigmes
      Écris la même petite tâche dans les styles impératif, orienté objet, fonctionnel et déclaratif, compare ce que chaque version facilite et reconnais les langages multiparadigmes.
    • Choisis un langage de programmation adapté à une tâche
      Évalue les facteurs qui déterminent si un langage convient à un projet (plateforme, domaine, performances, sûreté, écosystème et personnes) et justifie un choix pour un scénario concret.

Langages et paradigmes de programmation : cours

6 chapitres

1

Du code source aux instructions exécutées

Un processeur exécute de petites instructions binaires, tandis que les humains écrivent généralement des programmes dans des langages construits autour du problème à résoudre. Des traducteurs relient ces deux niveaux. Suis un même calcul à travers ces couches, puis compare la construction d’un programme avant son exécution avec son interprétation ou la compilation de certaines parties pendant son exécution. Le chemin choisi influe sur la portabilité, le temps de démarrage, la vitesse et le moment où une erreur peut apparaître.

1.1 Trois niveaux de code de programme

Une ligne comme x = a + b désigne un calcul sans mentionner de registre du processeur. L’abstraction rend cela possible : un langage masque les détails de niveau inférieur pour que la personne qui programme puisse se concentrer sur le travail que le programme doit accomplir.

Ce que le processeur exécute

Le code machine se compose d’instructions codées sous forme de nombres binaires. L’architecture du jeu d’instructions d’un processeur définit ses instructions et leur encodage. x86-64 est courant dans les PC et les serveurs ; ARM est courant dans les téléphones, les tablettes et de nombreux ordinateurs portables. Le code construit pour une architecture doit être traduit ou émulé pour fonctionner sur une autre.

L’hexadécimal permet d’afficher les octets des instructions de façon plus courte. Sur x86-64, B8 01 00 00 00 place 1 dans le registre EAX. Les octets 01 D8 ajoutent la valeur d’EBX à EAX. Les registres sont de petits emplacements de stockage à l’intérieur du processeur. Chaque instruction effectue une petite action, comme déplacer des données, additionner, comparer ou effectuer un saut. Le cycle de lecture-décodage-exécution exécute ces instructions.

Des noms pour les mêmes petites actions

Le langage d’assemblage remplace les octets des instructions par des noms lisibles appelés mnémoniques. Les deux instructions x86-64 peuvent s’écrire :

mov eax, 1
add eax, ebx

Un assembleur convertit le langage d’assemblage en code machine. Une ligne d’instruction ordinaire produit normalement une instruction machine. Les noms de registres et les instructions restent propres à une architecture du jeu d’instructions : utiliser des mots au lieu d’octets ne rend donc pas le langage d’assemblage portable.

Décrire un problème en moins de lignes

Un langage de haut niveau fournit des variables, des expressions, des conditions, des boucles et des fonctions. Python, C, Java et JavaScript permettent de décrire des opérations sans sélectionner de registres. Une instruction nécessite généralement plusieurs instructions machine. Une traduction simplifiée de x = a + b effectuerait les étapes suivantes :

  1. Charger la valeur de a dans un registre.
  2. Charger la valeur de b dans un autre registre.
  3. Additionner les valeurs des registres.
  4. Stocker le résultat pour x.

Un compilateur réel peut combiner ou réorganiser ces étapes tout en conservant le comportement requis.

NiveauCe que la personne qui programme écritQui gère les détails du matériel ?
Code machineInstructions codées du processeurLa personne qui programme ou le générateur de code
Langage d’assemblageMnémoniques et registresLa personne qui programme, avec un assembleur qui code les instructions
Code de haut niveauVariables et opérations sur les données du problèmeUn compilateur, un interpréteur ou une combinaison des deux

Le code source de haut niveau est plus facile à lire, à modifier et à vérifier. Un code source C portable peut être compilé une première fois pour un système x86-64, puis pour un système ARM. Chaque version construite nécessite tout de même une prise en charge adaptée du système d’exploitation et des bibliothèques. La portabilité du code source ne rend pas un exécutable natif universel.

Pourquoi les niveaux inférieurs restent utiles

Le langage d’assemblage reste utile dans le code de démarrage, certaines routines des systèmes d’exploitation et des pilotes, les petits systèmes embarqués et l’analyse de sécurité des programmes dont le code source n’est pas disponible. Pour le code applicatif courant, les compilateurs modernes produisent généralement du code machine dont les performances sont comparables à celles du langage d’assemblage écrit à la main.

Le passage vers des niveaux plus élevés s’est fait progressivement. Le langage d’assemblage s’est répandu à la fin des années 1940 et au début des années 1950. Le système A-0 de Grace Hopper, en 1952, a contribué à faire progresser la traduction automatique. IBM a publié FORTRAN en 1957 ; COBOL a suivi en 1959 et Lisp est apparu vers 1958-1960. « Haut niveau » désigne l’éloignement des détails du matériel, et non la difficulté d’apprentissage du langage.

Points clés

  • Un processeur exécute les instructions machine définies par son architecture du jeu d’instructions.
  • Un assembleur code les instructions en langage d’assemblage ; ce langage reste lié à une architecture du jeu d’instructions.
  • Les instructions de haut niveau masquent les étapes propres au matériel et deviennent généralement plusieurs instructions machine.
  • Un code source portable peut être traduit pour différentes cibles ; le code machine natif est propre à une cible.
  • Le code de bas niveau reste utile lorsque l’accès au matériel ou l’analyse l’exige.

1.2 Comment un compilateur construit un programme

Un compilateur traduit le code source en code cible. La cible peut être du code machine natif ou une forme intermédiaire comme le bytecode. La compilation anticipée a lieu avant l’exécution du programme ; la compilation à la volée a lieu pendant son exécution.

Du texte aux instructions

Pour une construction anticipée, plusieurs étapes transforment un fichier source en instructions :

  1. L’analyse lexicale découpe le texte en unités lexicales : des éléments porteurs de sens comme les noms, les opérateurs et les nombres.
  2. L’analyse syntaxique vérifie la grammaire et construit une structure qui montre comment les éléments s’assemblent.
  3. L’analyse sémantique vérifie les règles liées au sens, notamment les déclarations et les combinaisons de types autorisées.
  4. L’optimisation trouve une façon plus efficace d’effectuer le travail demandé.
  5. La génération de code produit les instructions cibles.

Considère l’instruction C total = price * 3;. Ses six unités lexicales sont total, =, price, *, 3 et ;. Les noms sont des identifiants, 3 est un littéral entier et le point-virgule termine l’instruction. L’analyse syntaxique indique que la multiplication fait partie de l’affectation : calculer price * 3, puis stocker sa valeur dans total.

Ces étapes ont des rôles différents. Une suite d’unités lexicales correctement écrites peut tout de même enfreindre la grammaire. Une instruction grammaticalement correcte peut tout de même employer un type de manière incorrecte. Trouver les unités lexicales ne suffit pas à établir qu’un programme est valide.

Effectuer le même travail à moindre coût

Supposons que le code source contienne seconds_per_day = 60 * 60 * 24. Les facteurs sont des constantes, donc le compilateur peut multiplier 60 par 60 pour obtenir 3 600, puis multiplier par 24 pour obtenir 86 400 pendant la construction. Il peut placer la valeur finale dans le résultat produit au lieu de répéter la multiplication à l’exécution. C’est le repliement des constantes.

Une optimisation doit préserver le comportement exigé par le langage. Elle ne peut pas remplacer un calcul par un autre plus rapide qui donnerait des résultats différents de ceux requis. L’objectif est de réduire le travail, le temps ou la mémoire nécessaires tout en conservant le sens du programme.

Assembler les éléments

Les grands programmes sont généralement construits à partir de nombreux fichiers. Un compilateur peut produire un fichier objet pour chaque fichier source. Un fichier objet contient du code traduit qui peut encore nécessiter des liens avec d’autres codes. Un éditeur de liens réunit les éléments et résout les appels aux routines de bibliothèques, comme les fonctions d’affichage ou de calcul.

Méthode d’édition de liensOrigine du code de bibliothèque à l’exécution
Édition de liens statiqueCode inclus dans l’exécutable pendant la construction
Édition de liens dynamiqueBibliothèque partagée compatible chargée au démarrage ou pendant l’exécution

L’exécutable obtenu est construit pour une plateforme cible, notamment une architecture du jeu d’instructions et des conventions de système d’exploitation. L’utilisateur n’a pas besoin du compilateur anticipé pour l’exécuter. Des bibliothèques partagées ou des composants d’exécution peuvent toutefois rester nécessaires.

Comprendre ce que prouve une construction réussie

En Java, int count = "five"; échoue à une vérification de type avant l’exécution. Du texte ne peut pas initialiser cette variable entière. Le programme doit réussir les vérifications requises à la compilation avant qu’une version exécutable soit disponible.

D’autres défauts subsistent après la compilation. Une division par un zéro saisi par l’utilisateur dépend des données d’exécution. Un programme censé effectuer une soustraction mais qui effectue une addition peut s’exécuter et renvoyer une valeur incorrecte. La compilation vérifie les règles du langage ; les tests doivent aussi vérifier que le résultat correspond à la tâche prévue.

Points clés

  • La compilation traduit le code source en code cible, qui n’est pas nécessairement du code machine.
  • Les étapes du compilateur vérifient les unités lexicales, la structure et le sens avant de produire les instructions cibles.
  • L’optimisation préserve le comportement requis ; le repliement des constantes évalue les constantes pendant la construction.
  • Un éditeur de liens relie les fichiers objets et le code des bibliothèques.
  • Une construction réussie peut encore contenir des erreurs d’exécution ou de logique.

1.3 Interprétation, bytecode et compilation à la volée

Un programme n’a pas nécessairement besoin de devenir un exécutable natif avant qu’un utilisateur le lance. Un interpréteur exécute le programme par l’intermédiaire d’un autre programme en cours d’exécution. De nombreuses implémentations combinent interprétation et compilation ; la question utile est donc de savoir comment le système considéré exécute le code.

Exécuter au moyen d’un interpréteur

Un interpréteur doit être disponible chaque fois qu’il exécute le programme. Une REPL interactive répète quatre actions : lire une entrée, l’évaluer, afficher un résultat et recommencer pour l’entrée suivante. Elle fournit un retour rapide lorsqu’on essaie de petites expressions.

Interpréter ne signifie pas nécessairement lire le code source brut une ligne à la fois. CPython, l’implémentation standard de Python, compile d’abord un fichier en bytecode. Une machine virtuelle logicielle exécute ensuite ces instructions internes. Le bytecode appartient à cet environnement d’exécution ; il ne s’agit pas du jeu d’instructions natif du processeur.

Pourquoi certaines erreurs apparaissent plus tôt

Compare deux fichiers Python. Celui-ci a une syntaxe valide :

print("start")
x = 10
print(x + "5")

Il affiche « start », associe x à 10, puis déclenche TypeError. L’addition de cet entier à une chaîne de caractères échoue uniquement lorsque l’expression est exécutée. Une opération similaire dans une branche non parcourue peut passer inaperçue jusqu’à l’exécution de cette branche.

Supprime maintenant la parenthèse fermante de la dernière ligne. CPython ne peut pas compiler le fichier, car sa syntaxe est incomplète. Il signale une erreur de syntaxe avant l’exécution de toute instruction du fichier, donc « start » n’est pas affiché. La vérification syntaxique du fichier entier et la vérification à l’exécution d’une opération exécutée ont lieu à des moments différents.

Partager le bytecode, spécialiser pendant l’exécution

Java illustre une approche hybride courante. javac Hello.java produit Hello.class, qui contient du bytecode. java Hello lance une machine virtuelle Java compatible. Différentes implémentations de cette machine virtuelle permettent à ce bytecode de s’exécuter sur différentes familles de processeurs. Le bytecode est portable entre environnements d’exécution compatibles ; chaque environnement doit lui-même fonctionner sur sa plateforme hôte. C# cible de la même façon un langage intermédiaire utilisé par .NET.

Un compilateur à la volée produit du code machine pendant l’exécution du programme. L’environnement d’exécution HotSpot de Java commence généralement par interpréter le code et compile les méthodes fréquemment utilisées. Les moteurs JavaScript des navigateurs combinent aussi des techniques d’exécution, dont la compilation à la volée. Compiler les parties très sollicitées peut améliorer la vitesse sur la durée, mais utilise du temps et de la mémoire pendant l’exécution.

ApprocheAvantage utileCoût à considérer
Construction native anticipéeTravail traduit avant le lancement ; aucun compilateur nécessaire pour l’utilisateurVersions compatibles distinctes pour les plateformes cibles
InterprétationRetour rapide après modification et exécution avec un interpréteur installéLe travail répété à l’exécution peut ralentir le code riche en calculs
Bytecode avec machine virtuelle et compilation à la voléeCode intermédiaire portable avec optimisation à l’exécutionInstallation de l’environnement, temps de mise en régime et mémoire supplémentaires

Une boucle qui additionne un milliard de valeurs s’exécute généralement beaucoup plus vite en C compilé que dans CPython : CPython effectue le travail d’interprétation et vérifie les types autour de nombreuses opérations. Pour un script qui renomme 20 fichiers, l’effort de développement peut compter bien davantage que cette différence. Mesure la charge de travail avant de te servir de la désignation d’un langage pour prévoir sa vitesse.

Compilé et interprété décrivent des choix d’implémentation. CPython utilise l’interprétation du bytecode ; PyPy utilise des techniques de compilation à la volée. Un langage peut avoir plusieurs implémentations, et une implémentation peut utiliser plusieurs méthodes d’exécution.

Points clés

  • Un interpréteur exécute un programme et doit être présent pour le faire fonctionner.
  • CPython compile un fichier en bytecode avant de l’exécuter.
  • Une erreur de syntaxe peut empêcher le démarrage d’un fichier ; une erreur d’exécution apparaît lorsque l’opération concernée s’exécute.
  • Un compilateur à la volée crée du code natif pendant l’exécution, souvent pour les parties fréquemment utilisées.
  • Les méthodes d’exécution dépendent des implémentations et les performances dépendent de la charge de travail.
2

Lis de courts programmes pour comprendre ce qu’ils font

Lire du code soulève deux questions : sa forme respecte-t-elle les règles du langage et que signifient ses opérations ? Une expression d’apparence identique peut se comporter différemment dans deux langages. Apprends à classer les erreurs, puis suis les affectations, les décisions, les boucles et les appels de fonctions dans leur ordre d’exécution. Un petit tableau des valeurs qui changent rend visible la différence entre ce que la personne qui programme voulait obtenir et ce que le programme fait réellement.

2.1 Syntaxe, sens et trois types d’erreurs

La syntaxe fixe les règles qui déterminent si le texte d’un programme est valide. La sémantique donne son sens à ce texte. Un programme peut respecter toutes les règles de grammaire et tout de même calculer un résultat incorrect : vérifier son apparence n’est donc que la première étape de sa lecture.

Reconnaître les éléments et la grammaire

Les unités lexicales comprennent les mots-clés comme if, les identifiants comme total, les littéraux comme 3 ou "hello", les opérateurs comme + et ==, et la ponctuation. Une grammaire décrit comment ces éléments peuvent former des expressions et des instructions.

Python exige des deux-points après if x > 3: et utilise l’indentation pour marquer les instructions contrôlées par cette condition. C, Java et JavaScript utilisent des accolades pour les blocs ; C et Java exigent aussi des points-virgules après de nombreuses instructions. Dans ces langages, l’indentation aide le lecteur humain, mais ne détermine pas le bloc.

Des syntaxes différentes peuvent exprimer une action similaire. Ces instructions affichent un message de salutation :

print("hello")
printf("hello\n");
System.out.println("hello");

La séquence d’échappement C \n ajoute un saut de ligne. Connaître l’idée de l’affichage aide à apprendre la façon dont chaque langage l’exprime.

Les mêmes symboles peuvent avoir des sens différents
Expression et langageRésultatRègle
7 / 2 en Python ou JavaScript3.5La division produit un résultat fractionnaire
7 / 2 avec des opérandes entiers en C ou Java3La division entière tronque vers zéro
-7 // 2 en Python-4La division avec arrondi inférieur arrondit vers le bas
-7 / 2 avec des entiers en C-3La troncature se fait vers zéro
"3" * 4 en Python"3333"Une chaîne de caractères est répétée
"3" * 4 en JavaScript12Le texte est converti en nombre pour la multiplication

L’apparence familière d’un symbole ne détermine pas son sens d’un langage à l’autre.

L’ordre d’évaluation compte aussi. La priorité fait que la multiplication et la division sont effectuées avant l’addition. Avec a = 4 et b = 6, average = a + b / 2 divise d’abord 6 par 2, puis ajoute 4, ce qui produit 7,0. Les parenthèses dans average = (a + b) / 2 font effectuer la somme en premier et donnent 5,0.

Distinguer les erreurs

Une erreur de syntaxe enfreint la grammaire, comme des deux-points manquants en Python. Une erreur d’exécution se produit lors d’une opération exécutée, comme une division par zéro en Python. Une erreur de logique laisse l’exécution se dérouler, mais donne une réponse non voulue, comme dans le calcul incorrect de la moyenne.

Certaines expressions valides sautent volontairement une opération. En Python, x != 0 and 10 / x > 2 utilise l’évaluation en court-circuit. Lorsque x vaut zéro, la première condition est fausse, donc la partie droite n’est pas évaluée et aucune division n’a lieu. Inverser ces conditions supprimerait cette protection. Python écrit and, or et not ; C, Java et JavaScript utilisent &&, || et !.

L’affectation et la comparaison constituent une autre distinction importante. = stocke une valeur ; == teste l’égalité. C autorise if (x = 5) : cette instruction affecte 5 et traite cette valeur non nulle comme vraie. Python rejette if x = 5: comme une syntaxe invalide. La même erreur peut donc être détectée à une étape différente selon le langage.

Points clés

  • La syntaxe définit la forme valide ; la sémantique définit le sens.
  • Les opérateurs peuvent avoir des sens différents selon les langages.
  • La priorité et les parenthèses déterminent les opérations regroupées.
  • Les erreurs de syntaxe, d’exécution et de logique nécessitent des vérifications différentes.
  • L’évaluation en court-circuit peut éviter une opération dangereuse.
  • L’affectation stocke une valeur ; la comparaison d’égalité teste des valeurs.

2.2 Suis les valeurs à travers le déroulement du programme et les appels

Un court programme est plus facile à comprendre lorsque tu suis une instruction exécutée à la fois. Garde les valeurs actuelles visibles au lieu de passer directement de la première ligne à un résultat supposé.

Expressions et affectations

Une expression produit une valeur : price * 3, x > 10 et len(name) en sont des exemples. Une instruction effectue une action, comme affecter une valeur ou contrôler un bloc. Dans total = price * 3, l’expression de droite est évaluée avant que son résultat soit affecté à total.

Une variable est un nom qui fait référence à une valeur. L’affectation donne au nom sa valeur actuelle ; elle n’affirme pas, comme une équation mathématique, que les deux membres ont toujours été égaux. Si count contient 4, count = count + 1 lit d’abord 4, ajoute 1 et associe count à 5.

Suivre la prochaine instruction exécutée

Trois structures organisent le déroulement du programme :

  • La séquence exécute les instructions dans l’ordre.
  • La structure conditionnelle utilise une condition pour choisir un chemin, par exemple avec if et else.
  • L’itération répète un bloc au moyen d’une boucle for ou while.

Le théorème de la programmation structurée, associé à Böhm et Jacopini en 1966, montre que ces structures peuvent exprimer n’importe quel algorithme. Une boucle for parcourt généralement les éléments d’une collection. Une boucle while se répète tant que sa condition reste vraie.

Considère cette boucle Python :

total = 0
for n in [3, 8, 5]:
    total = total + n

L’affectation indentée s’exécute une fois pour chaque élément. Un tableau de suivi d’exécution indique l’état après chaque mise à jour :

Momentn actueltotal
Avant la bouclePas encore d’élément0
Première mise à jour33
Deuxième mise à jour811
Troisième mise à jour516

Chaque nouveau total utilise le précédent. Réinitialiser total à l’intérieur de la boucle produirait un calcul différent, car les additions précédentes seraient perdues.

Entrer dans une fonction et en sortir

Une fonction regroupe une tâche sous un nom. Un paramètre est un nom dans la définition ; un argument est une valeur fournie par un appel :

def area(width, height):
    return width * height

result = area(3, 4)

L’appel associe width à 3 et height à 4. La multiplication produit 12 et return le transmet à l’appelant. L’exécution reprend à l’affectation, donc result reçoit 12. Ces noms décrivent une tâche qui peut être réutilisée avec d’autres arguments.

Un nom a une portée

La portée détermine où un nom peut être utilisé. Les noms créés dans une fonction sont normalement locaux à cet appel. Un nom défini au niveau principal est global à son module. Des appels distincts ont des associations locales distinctes, même lorsqu’ils utilisent des noms identiques.

count = 7

def reset():
    count = 0

reset()
print(count)

Ce code affiche 7. L’affectation dans reset crée une variable locale count ; elle ne remplace pas l’association globale. Une fonction peut plutôt renvoyer un résultat et laisser l’appelant l’affecter.

La portée et la durée de vie répondent à des questions différentes. Un nom local n’est pas accessible au code extérieur simplement parce que l’objet auquel il faisait référence existe encore. Les objets renvoyés peuvent rester en vie, et une fonction imbriquée renvoyée peut conserver l’accès aux variables de l’appel qui l’englobe ; cet environnement conservé fait partie d’une fermeture lexicale.

Points clés

  • Évalue l’expression de droite avant de mettre à jour la cible d’une affectation.
  • Suis la séquence, les structures conditionnelles et les itérations dans leur ordre réel d’exécution.
  • Les paramètres sont des noms dans la définition ; les arguments sont les valeurs fournies.
  • Une valeur de retour transmet un résultat à l’appelant.
  • Les noms locaux et globaux peuvent avoir des associations distinctes ; la portée diffère de la durée de vie.
3

Types, références et mémoire

Les valeurs ont besoin d’un type, d’un emplacement en mémoire et d’une durée de vie. Ces choix expliquent pourquoi un texte qui ressemble à un nombre peut refuser les opérations arithmétiques, pourquoi deux noms peuvent donner accès à la même liste modifiée et pourquoi des données inutilisées restent parfois allouées. Compare les vérifications effectuées par les langages, suis un appel de fonction en mémoire et examine trois façons de libérer de l’espace. Chaque approche modifie l’équilibre entre contrôle, facilité d’utilisation et sûreté.

3.1 Les types déterminent quelles opérations ont un sens

Un type de données définit les valeurs possibles et les opérations qu’elles permettent. Un entier permet les opérations arithmétiques sur les nombres entiers ; un booléen représente vrai ou faux. Les chaînes de caractères, les listes, les enregistrements et les objets combinent ou organisent d’autres valeurs. Les types déterminent aussi la façon dont les valeurs sont représentées en mémoire, même si la taille d’une valeur peut dépendre de cette valeur et de l’implémentation.

Quand les types sont-ils vérifiés ?
ApprocheMoment des vérificationsExemple
Typage statiqueAvant l’exécutionJava rejette int count = "five";
Typage dynamiquePendant l’exécutionPython vérifie si une opération exécutée convient à ses opérandes

En Python, count = 5 suivi de count = "five" est valide. Le nom fait désormais référence à une chaîne de caractères. L’évaluation de count + 1 déclenche alors TypeError. Le typage dynamique donne des types définis aux valeurs ; il ne supprime pas les types.

Le typage statique n’exige pas une indication de type visible sur chaque variable. L’inférence de types permet à un compilateur de déduire un type à partir du code environnant. Kotlin déduit un entier pour val n = 5, et Rust peut déduire un entier pour let n = 5;. Les annotations de type facultatives de Python sont différentes : des outils distincts comme mypy peuvent les vérifier, mais l’exécution ordinaire de Python ne les impose pas.

Quand les valeurs sont-elles converties ?

Le moment de la vérification et la rigueur des conversions sont deux questions distinctes. Python rejette "3" + 4 ; la conversion explicite dans int("3") + 4 donne 7. JavaScript applique différentes règles de conversion automatique selon les opérateurs :

  • "3" + 4 donne la chaîne "34".
  • "3" * 4 donne le nombre 12.
  • "3" - 4 donne le nombre -1.

La conversion automatique est appelée conversion implicite. Les termes typage « fort » et « faible » sont des façons informelles de parler de la rigueur des conversions, et non d’autres noms pour le typage statique et dynamique. Python est dynamique et strict pour l’addition de types différents ci-dessus ; C est statique et autorise tout de même de nombreuses conversions numériques implicites.

La fonction input() de Python renvoie une chaîne de caractères. Si une personne saisit « 30 », le résultat est toujours de type str. age + 1 échoue, tandis que int(age) + 1 donne 31 lorsque age contient ce texte. Ce qui apparaît à l’écran ne suffit pas à déterminer le type.

La représentation des nombres a des limites

Un entier signé sur 32 bits représente les valeurs de −2 147 483 648 à 2 147 483 647. Le type int de Java reboucle de la valeur maximale à la valeur minimale lorsqu’on ajoute 1. En C, le dépassement de capacité d’un entier signé est un comportement indéfini : il ne faut donc pas supposer que le même rebouclage se produit. Les entiers Python grandissent autant que la mémoire le permet : 2 ** 100 vaut exactement 1 267 650 600 228 229 401 496 703 205 376.

Les nombres à virgule flottante représentent la plupart des fractions décimales de manière approximative en binaire. En Python et JavaScript, 0.1 + 0.2 produit 0.30000000000000004. Comparer directement ce résultat à 0.3 donne faux.

Lorsqu’un calcul permet une petite erreur, compare les valeurs à l’aide d’une tolérance choisie pour la tâche, par exemple abs(a - b) < 1e-9. Une tolérance absolue aussi petite ne convient qu’à certaines échelles. Pour une comptabilité décimale exacte, utilise des nombres entiers dans la plus petite unité ou un type décimal adapté. Modifier la comparaison ne peut pas rendre exacte une représentation approximative.

Points clés

  • Un type définit des valeurs et les opérations qu’elles permettent.
  • Le typage statique et dynamique décrit le moment des vérifications, tandis que la conversion implicite concerne les règles de conversion.
  • L’inférence de types prend en charge le typage statique ; les annotations Python nécessitent des outils de vérification distincts.
  • Les entiers de taille fixe peuvent subir un dépassement de capacité, dont le comportement dépend du langage.
  • L’arrondi en virgule flottante influe sur les tests d’égalité et le choix de la représentation des nombres.

3.2 Suis les noms, les objets et les appels de fonctions en mémoire

La mémoire principale stocke les données d’un programme pendant son exécution. Chaque octet possède une adresse. Les langages fournissent des noms pour permettre aux personnes qui programment de manipuler des valeurs sans écrire d’adresses brutes à chaque opération.

Les appels utilisent des blocs d’activation ; les objets ont une durée de vie

En C, un int occupe généralement 4 octets et &x obtient l’adresse de x. Les langages de plus haut niveau masquent souvent les adresses derrière des références. Il faut distinguer l’association qui nomme un objet de l’objet lui-même.

La pile d’appels assure le suivi des appels de fonctions actifs. Chaque appel possède un bloc d’activation contenant des informations comme les paramètres, les associations locales et l’endroit où l’exécution doit reprendre après le retour. Si main appelle area(3, 4), un bloc pour area est ajouté au-dessus de celui de l’appelant. La fonction calcule 12, se termine et son bloc est retiré. Pour les appels imbriqués ordinaires, l’appel le plus récent se termine en premier.

La récursivité ajoute un appel pour chaque niveau non terminé. Si les appels continuent de s’accumuler, l’espace disponible dans la pile peut s’épuiser. Python déclenche normalement RecursionError après environ 1 000 appels imbriqués, la limite dépendant de ses paramètres. Une condition d’arrêt doit être atteinte pour qu’un calcul récursif fini se termine.

Le tas contient les objets alloués dynamiquement, comme une liste qui grandit ou un objet qui survit à la fonction qui l’a créé. L’allocation mémoire réserve de l’espace de stockage. Une liste renvoyée peut rester sur le tas après la fin de l’appel qui l’a créée.

Copier une référence partage un objet

Une référence désigne un objet. Un pointeur en C ou C++ représente explicitement une adresse utilisée pour accéder à des données. Copier une référence copie l’accès à l’objet, et non toutes les données de l’objet.

a = [1, 2]
b = a
b.append(3)

Après la première ligne, il existe une liste. La deuxième ligne lui associe un autre nom. La dernière ligne modifie cette liste : a et b montrent donc toutes deux [1, 2, 3]. Ce partage est appelé aliasing. Utiliser b = a.copy() créerait une liste externe distincte ; pour cette liste d’entiers, ajouter un élément à la nouvelle liste laisserait a inchangée.

Compare l’affectation et la mutation :

x = 5
y = x
y = y + 1

L’entier 5 est immuable. L’addition produit 6, et seul y est associé à une nouvelle valeur. x fait toujours référence à 5. Les nombres, chaînes de caractères et tuples de Python sont immuables ; les listes et les dictionnaires sont mutables. Le contenu fixe d’un tuple peut comprendre une référence à une liste mutable : l’immuabilité du conteneur externe ne fige donc pas tous les objets qu’il contient.

Appliquer les mêmes règles aux arguments

Lorsque Python transmet une liste à une fonction, le paramètre fait référence au même objet. Appeler items.append(4) dans la fonction modifie cette liste. L’affectation items = [] associe uniquement le nom du paramètre local à une nouvelle liste. Le nom de l’appelant reste associé à l’objet d’origine.

C transmet les valeurs des arguments par valeur. Pour permettre à une fonction de modifier une variable de l’appelant par son adresse, transmets un pointeur vers cette variable. Dans chaque langage, pose-toi deux questions distinctes pendant le suivi : « À quel objet ce nom fait-il référence ? » et « Cette opération modifie-t-elle cet objet ou associe-t-elle un nom à autre chose ? »

Points clés

  • La pile d’appels suit les appels non terminés ; le tas contient les objets alloués dynamiquement.
  • Un objet peut survivre à la fonction qui l’a créé.
  • Copier une référence partage un objet ; cela ne duplique pas ses données.
  • Une mutation modifie un objet, tandis qu’une nouvelle association change l’objet auquel un nom fait référence.
  • Le passage d’arguments suit la même distinction entre objets partagés et associations locales.

3.3 Libère la mémoire sans perdre les données encore utilisées

La mémoire allouée doit finir par devenir réutilisable. La difficulté consiste à libérer les données après leur dernière utilisation tout en conservant chaque objet dont le programme a encore besoin. Les langages confient cette responsabilité à la personne qui programme, à un système d’exécution ou à des règles de possession vérifiées par le compilateur.

Gestion manuelle et erreurs possibles

En C, malloc(n) demande n octets et renvoie, en cas de succès, un pointeur vers le bloc. free(p) libère un bloc obtenu par allocation. La personne qui programme doit l’utiliser au bon moment et exactement une fois. C++ prend aussi en charge la gestion manuelle, même si ses outils standards peuvent automatiser la libération grâce aux durées de vie des objets.

Plusieurs erreurs ont des causes différentes :

ProblèmeCe qui s’est mal passé
Fuite mémoireUn espace alloué devenu inutile n’est pas libéré
Utilisation après libérationUn pointeur est utilisé après la libération de l’espace vers lequel il pointe
Double libérationLe même bloc alloué est libéré deux fois
Débordement de tamponUne écriture dépasse les bornes d’un bloc de mémoire

Un pointeur qui continue de faire référence à un espace libéré est un pointeur pendant. L’espace peut être réutilisé pour autre chose : y accéder peut donc corrompre des données ou faire planter le programme.

Pour un tableau C int scores[10], les positions valides vont de 0 à 9. Écrire dans scores[10] dépasse les bornes du tableau, et C ne fournit aucune vérification automatique de ces bornes. Python déclenche IndexError lorsque l’indice 10 est utilisé sur une liste de dix éléments, ce qui empêche cet accès d’écraser la mémoire adjacente.

Suivre l’accessibilité ou compter les références

Un ramasse-miettes par traçage part de racines actives, comme les références en cours d’utilisation, suit les liens entre les objets et peut récupérer les objets inaccessibles. Java, C#, Go et JavaScript utilisent des approches par traçage. Le ramassage demande du travail et de la mémoire à l’exécution ; selon le ramasse-miettes, il peut entraîner des pauses.

Le comptage de références suit les références à un objet. Pour une nouvelle liste Python, la séquence simplifiée est la suivante : a = [1, 2] fournit une référence ; b = a en fournit une deuxième ; del a en supprime une ; b = None supprime l’autre. CPython récupère normalement cette liste ordinaire lorsque son compteur atteint zéro.

Un groupe d’objets peut maintenir les compteurs de chacun au-dessus de zéro même lorsqu’aucune partie active du programme ne peut atteindre le groupe. C’est un cycle de références. CPython complète le comptage de références par un ramassage des cycles. Le comptage de références et le traçage sont deux techniques de gestion automatique de la mémoire.

Vérifier la possession avant l’exécution

Rust donne à chaque valeur un propriétaire et vérifie les règles d’emprunt des références. Lorsque la possession prend fin, le code de nettoyage généré libère la valeur. Le Rust sûr rejette les utilisations de références susceptibles de survivre à la validité de leur cible, sans nécessiter de ramasse-miettes par traçage. Swift utilise un comptage automatique de références inséré par le compilateur.

La sûreté mémoire ne concerne pas seulement les plantages. Microsoft a indiqué en 2019 qu’environ 70 % des vulnérabilités auxquelles l’entreprise attribuait chaque année des CVE concernaient la sûreté mémoire ; Chromium a indiqué une proportion comparable de bogues graves en 2020. En décembre 2023, la CISA, la NSA, le FBI et des partenaires internationaux ont encouragé les éditeurs de logiciels à prévoir une transition vers des langages à sûreté mémoire.

La récupération automatique conserve tout de même les objets accessibles. Une liste globale qui ne cesse de grandir peut garder tous les anciens enregistrements en vie même après que l’application a fini de les utiliser. Un ramasse-miettes ne peut pas décider que ces enregistrements accessibles sont devenus inutiles à la tâche ; le programme doit cesser de les conserver.

Points clés

  • L’allocation manuelle exige une libération au bon moment.
  • Les fuites, l’utilisation après libération, la double libération et le débordement de tampon ont des causes distinctes.
  • Le traçage trouve les objets inaccessibles ; le comptage de références réagit à la disparition de références.
  • CPython ajoute le ramassage des cycles au comptage de références.
  • La possession peut organiser la libération sans ramasse-miettes par traçage.
  • Le ramassage automatique ne peut pas récupérer les objets que le programme conserve encore.
4

Organise les changements d’état et la coopération entre objets

Une façon de décrire un programme consiste à présenter les commandes qui mettent à jour des valeurs. Une autre regroupe ces valeurs avec les opérations qui les contrôlent. Ces points de vue fonctionnent souvent ensemble : une méthode d’objet peut contenir une boucle ordinaire. Suis d’abord les changements d’état, puis construis un petit modèle de compte pour comprendre comment les classes protègent les règles et comment l’héritage, le polymorphisme et la composition permettent à des objets apparentés de partager une interface sans leur attribuer des responsabilités inadaptées.

4.1 Commandes, procédures et état partagé

Un paradigme de programmation est une façon d’organiser le code et de penser le calcul. En programmation impérative, un programme donne des commandes qui modifient son état : les valeurs actuellement contenues dans les variables et les objets.

Rendre les changements d’état visibles

Un total cumulé est un calcul impératif simple :

total = 0
for n in [3, 8, 5]:
    total = total + n

L’état initial est total = 0. L’ajout de 3 le fait passer à 3. L’ajout de 8 le fait passer à 11. L’ajout de 5 le fait passer à 16. Chaque affectation utilise l’état laissé par l’étape précédente : l’ordre fait donc partie de la méthode.

Ce style correspond aux modifications successives que le processeur apporte aux registres et à la mémoire. Il convient aussi aux algorithmes qui mettent volontairement à jour une collection en place, comme une routine de tri qui échange des éléments.

Donner des noms aux tâches

La programmation procédurale organise le code impératif en fonctions ou procédures. Un programme de rapport peut appeler read_scores(), average(scores) et print_report(avg). Ces noms permettent de voir la tâche globale sans suivre immédiatement chaque ligne interne. Chaque procédure peut être comprise et testée séparément.

C, Pascal, Fortran et COBOL sont des langages procéduraux classiques. Python prend aussi en charge ce style : un script peut utiliser des fonctions nommées sans définir de classes.

La programmation structurée organise le déroulement du programme à l’aide de séquences, de structures conditionnelles, de boucles et d’appels de procédures. Elle évite les sauts arbitraires qui rendent difficile la prévision de la prochaine instruction exécutée. La lettre d’Edsger Dijkstra de 1968, « Go To Statement Considered Harmful », a contribué à populariser cette approche. Les sorties contrôlées comme break, continue et return restent utiles : elles permettent de quitter une itération donnée, une boucle ou une fonction.

Les effets peuvent dépasser la valeur de retour

Un effet de bord modifie quelque chose au-delà du résultat renvoyé par une fonction. Afficher du texte, enregistrer un fichier, envoyer un message réseau et modifier des données partagées sont des effets de bord. Les programmes ont besoin de ces actions pour interagir avec le monde.

Des problèmes apparaissent lorsque des parties du code sans lien apparent modifient le même état sans le signaler. Supposons que apply_discount() réduise une variable globale price de 10 % et que add_shipping() ajoute 5. En partant de 100, appliquer d’abord la remise donne 90, puis l’ajout des frais de livraison donne 95. Ajouter d’abord les frais donne 105, puis la remise donne 94,5. Les deux procédures effectuent correctement leurs propres étapes, mais le résultat combiné dépend de leur ordre.

Transmettre des valeurs et renvoyer de nouveaux résultats peut rendre cet ordre visible :

price = add_shipping(apply_discount(100))

L’appel intérieur s’exécute en premier : remise, puis frais de livraison. Une imbrication différente exprime une règle différente. Une circulation claire des données aide à voir si la remise doit aussi s’appliquer aux frais de livraison.

OrganisationQuestion principale à la lecture
Commandes impérativesQuelle valeur change ensuite ?
Procédures nomméesQuelle tâche cet appel effectue-t-il ?
Mises à jour globales partagéesQuelle action précédente a modifié cette valeur ?

Le code impératif reste courant dans les méthodes d’objets et les gestionnaires d’événements modernes. Choisir une conception globale orientée objet ou fonctionnelle n’empêche pas une petite partie d’utiliser des commandes directes lorsque ces étapes rendent l’algorithme clair.

Points clés

  • Le code impératif décrit des étapes qui modifient l’état enregistré.
  • Les procédures donnent des noms et des limites à des tâches cohérentes.
  • Un déroulement structuré rend les chemins d’exécution plus faciles à suivre.
  • Les effets de bord comprennent les modifications des données partagées et les interactions avec le monde extérieur.
  • Les mises à jour partagées peuvent rendre les résultats dépendants de l’ordre des appels.

4.2 Les classes réunissent les données et leurs règles

La programmation orientée objet modélise un programme sous la forme d’objets qui coopèrent. Chaque objet associe des données aux opérations sur ces données. Un compte peut contenir son titulaire et son solde tout en proposant des opérations de dépôt et de retrait.

Distinguer le modèle de ses instances

Une classe définit les attributs et les méthodes que ses objets fournissent. Un objet, ou une instance, est un élément particulier de cette classe. BankAccount est le modèle ; le compte d’Ana avec 150 et celui de Ben avec 40 sont des instances distinctes. Leurs soldes peuvent changer indépendamment.

Un attribut stocke des données associées à un objet. Une méthode est une opération appelée par l’intermédiaire d’un objet, comme acct.deposit(50). Dans une méthode d’instance Python, self fait référence à l’objet qui reçoit l’appel.

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("amount must be positive")
        self._balance = self._balance + amount

L’appel BankAccount("Ana", 100) construit une nouvelle instance. Sa méthode __init__ initialise les données de l’instance. Un constructeur est le processus de préparation d’un nouvel objet ; Python sépare la création de cette étape d’initialisation. L’appel acct.deposit(50) sur ce compte fait passer son solde de 100 à 150.

Placer la règle au point de mise à jour

L’encapsulation réunit les données et les opérations qui garantissent le respect de leurs règles. Supposons qu’un compte ne doive jamais avoir un solde négatif. La méthode de retrait vérifie la modification proposée avant de l’effectuer :

def withdraw(self, amount):
    if amount <= 0:
        raise ValueError("amount must be positive")
    if amount > self._balance:
        raise ValueError("insufficient funds")
    self._balance = self._balance - amount

Cette méthode appartient à BankAccount. À partir d’un solde de 150, withdraw(200) déclenche une erreur et laisse 150. withdraw(30) réussit et laisse 120. L’appel qui échoue n’effectue pas d’abord la soustraction pour découvrir ensuite le problème ; la vérification a lieu avant la mise à jour.

Les autres parties du code doivent utiliser ces méthodes au lieu d’affecter un solde sans vérification. Java et C# peuvent limiter l’accès direct avec private. En Python, le trait de soulignement initial dans _balance signale par convention un détail interne ; il n’impose pas la confidentialité.

Le masquage de l’information va plus loin : les appelants dépendent des opérations proposées par une classe sans avoir besoin de connaître sa représentation interne. Le solde pourrait être stocké différemment par la suite, tandis que les opérations publiques de dépôt et de retrait conserveraient leur sens.

Laisser le problème suggérer les responsabilités

Un système de bibliothèque comporte des livres, des adhérents et des prêts. Ces noms suggèrent des classes ou des attributs possibles ; des actions comme emprunter et renouveler suggèrent des méthodes. Choisis une responsabilité claire pour chaque classe. Une classe de compte qui envoie aussi tous les courriels et met en forme tous les rapports devient plus difficile à modifier, car des tâches sans lien sont mélangées.

Les classes et les objets sont apparus dans Simula 67 en 1967 ; Smalltalk a contribué à diffuser cette approche dans les années 1970. Java, C#, C++ et Python prennent en charge la programmation orientée objet, mais l’utilisation de l’un de ces langages n’impose pas d’organiser chaque programme en classes. La question de conception utile est de savoir quelles données et quelles règles vont ensemble.

Points clés

  • Une classe définit des attributs et des méthodes ; chaque instance possède ses propres données.
  • Les méthodes agissent sur l’objet qui reçoit l’appel.
  • L’encapsulation réunit la validation et les changements d’état.
  • En Python, les traits de soulignement signalent par convention les attributs internes ; certains langages imposent un accès privé.
  • Le masquage de l’information permet aux appelants de dépendre des opérations publiques d’une classe plutôt que de ses détails internes.
  • Une classe doit avoir une responsabilité cohérente.

4.3 Spécialise les comportements et combine les objets

Les objets ont souvent besoin de partager des opérations tout en différant dans les détails. L’héritage, le polymorphisme et la composition résolvent différentes parties de ce problème de conception.

Spécialiser un type d’objet existant

L’héritage définit une sous-classe à partir d’une superclasse. En Python, class SavingsAccount(BankAccount): donne à la classe spécialisée accès au comportement hérité du compte. Elle peut ajouter add_interest(rate) : un solde de 1 000 auquel on ajoute 2 % d’intérêts devient 1 020.

La relation recherchée est est un. Un compte d’épargne reste un compte bancaire et doit respecter les règles attendues des comptes bancaires. Réutiliser quelques lignes n’est pas une raison suffisante pour affirmer qu’un type d’objet en est un autre.

Une sous-classe peut redéfinir une méthode avec sa propre implémentation. Supposons qu’un compte courant facture des frais de 1 pour chaque retrait réussi. Avec un solde de 100, retirer 30 coûte 31 et laisse 69. Pour préserver la règle du compte, la méthode doit vérifier le retrait plus les frais avant de modifier le solde. Si ce coût total dépasse le solde, elle refuse toute l’opération.

Faire fonctionner un même appel avec différents objets

Le polymorphisme permet à une même opération de fonctionner sur différents types d’objets. Un cercle et un rectangle peuvent tous deux fournir area() :

for shape in shapes:
    print(shape.area())

La boucle n’a pas besoin d’un if pour chaque classe de forme. Chaque objet fournit sa propre implémentation. Le choix de cette implémentation pendant l’exécution est la liaison dynamique.

Pour Circle(2), l’aire est d’environ 12,57 unités carrées. Pour Rectangle(3, 4), elle est de 12 unités carrées. Leur aire totale est d’environ 24,57 unités carrées. Une classe Triangle ajoutée plus tard peut rejoindre la collection en fournissant une opération area() compatible ; la boucle elle-même n’a pas besoin d’une branche supplémentaire.

Une interface indique les opérations qu’un type doit proposer. L’interface Java interface Shape { double area(); } impose aux classes qui l’implémentent de fournir la méthode de calcul de l’aire. Une classe abstraite peut fournir une partie de l’implémentation commune tout en laissant d’autres méthodes aux sous-classes. Python utilise souvent le typage canard : un objet peut fonctionner dans la boucle s’il fournit l’opération requise, même sans superclasse commune.

Contenir un composant au lieu de le devenir

La composition stocke d’autres objets comme composants. Une voiture possède un moteur et des roues. Elle peut appeler la méthode start() de son moteur sans hériter de Engine.

RelationConception adaptéeRaison
Un compte d’épargne est un compteHéritageIl préserve le comportement attendu d’un compte
Une voiture possède un moteurCompositionLe moteur est un composant, pas la catégorie de la voiture
Différentes formes proposent le calcul de l’aireOpération ou interface partagéeL’appelant peut demander l’opération de manière uniforme

Les longues chaînes d’héritage peuvent propager les effets d’une modification de superclasse à de nombreuses classes. Une sous-classe qui doit désactiver un comportement hérité peut révéler une relation « est un » inadaptée. La composition rend souvent ces dépendances moins importantes et plus claires.

Les quatre piliers habituellement cités de la programmation orientée objet s’articulent ici : l’encapsulation protège les données au moyen d’opérations, l’abstraction masque les détails de l’implémentation, l’héritage spécialise un type et le polymorphisme permet à un appelant d’utiliser une opération partagée entre plusieurs implémentations.

Points clés

  • L’héritage modélise une relation « est un » et doit préserver le comportement attendu.
  • La redéfinition remplace l’implémentation d’une méthode dans une classe spécialisée.
  • Le polymorphisme permet à une opération d’appeler différentes implémentations.
  • Une interface indique les opérations requises ; le typage canard s’appuie sur les opérations fournies par un objet.
  • La composition modélise des objets qui contiennent d’autres objets comme composants.
5

Calcule de nouvelles valeurs et décris les résultats voulus

Un programme peut se concentrer sur les transformations de valeurs ou sur le résultat qu’un moteur doit produire. Les fonctions pures rendent les dépendances explicites, tandis que les données immuables évitent les modifications par l’intermédiaire de références partagées. Les requêtes déclaratives laissent un moteur spécialisé choisir les étapes d’exécution. Suis une chaîne de traitement de données et une requête de base de données pour voir ce que ces approches facilitent et où les entrées, les sorties et les instructions machine concrètes interviennent toujours.

5.1 Fonctions pures et chaînes de traitement des données

La programmation fonctionnelle construit des résultats en combinant des fonctions. Son principe central consiste à produire de nouvelles valeurs à partir des entrées tout en limitant les mutations partagées. Lisp a introduit des idées fonctionnelles largement utilisées vers 1958 ; Haskell, Erlang, Clojure, F# et OCaml développent ces idées de différentes façons.

Savoir ce qui rend une fonction pure

Une fonction pure donne le même résultat pour les mêmes arguments et n’a aucun effet de bord. def with_tax(price): return price * 1.2 illustre cette idée : with_tax(50) donne 60,0. Une fonction qui ajoute aussi un élément à une liste globale ou affiche un message a un effet au-delà de sa valeur de retour. Un résultat qui dépend de l’heure actuelle dépend lui aussi de plus que des arguments.

Un appel pur peut être remplacé par sa valeur de retour sans modifier le comportement du programme. Cette propriété est la transparence référentielle. Elle rend les tests isolés simples et permet à des appels indépendants de s’exécuter en parallèle sans chercher à mettre à jour le même état partagé.

L’immuabilité maintient une valeur existante inchangée. [n + 1 for n in nums] crée une nouvelle liste et laisse nums intacte. Un programme peut toujours représenter des informations qui changent en produisant de nouvelles versions ; il n’a pas besoin d’écraser l’ancienne version.

Transmettre un comportement comme une donnée

Les fonctions sont de première classe lorsqu’elles peuvent être affectées à des noms, transmises comme arguments et renvoyées comme valeurs. Une fonction d’ordre supérieur reçoit une fonction ou en renvoie une. Une expression lambda est une expression de fonction sans nom, comme lambda n: n * n.

Trois opérations d’ordre supérieur courantes forment une chaîne de traitement :

  • filter conserve les éléments qui satisfont un test.
  • map transforme chaque élément restant.
  • reduce combine les éléments en un seul résultat à l’aide d’un accumulateur.

Pour calculer la somme des carrés des nombres pairs de 1 à 6, le filtrage laisse 2, 4 et 6 ; leur élévation au carré donne 4, 16 et 36 ; leur somme donne 56.

sum(map(lambda n: n * n,
        filter(lambda n: n % 2 == 0, range(1, 7))))

Ici, % 2 == 0 teste si un nombre est pair, et range(1, 7) fournit les nombres de 1 à 6. Haskell exprime le même calcul par sum (map (^2) (filter even [1..6])).

Avec reduce importé de functools en Python, reduce(lambda acc, n: acc + n, [3, 8, 5], 0) combine 0 et 3 pour obtenir 3, puis ce résultat et 8 pour obtenir 11, puis ce résultat et 5 pour obtenir 16. La chaîne décrit des transformations sans boucle autour qui mettrait à jour un total partagé.

Conserver un environnement ou appeler la fonction sur un problème plus petit

def multiplier(k): return lambda x: k * x renvoie une fonction qui conserve l’accès à k. Ainsi, multiplier(3)(10) renvoie 30 même après la fin de l’appel extérieur. La fonction renvoyée et l’environnement conservé forment une fermeture lexicale.

La récursivité consiste pour une fonction à s’appeler elle-même sur un problème plus petit. La factorielle renvoie 1 lorsque n == 0 ; sinon, elle renvoie n * factorial(n - 1). Pour 5, les produits donnent 120. Un calcul fini nécessite un cas de base accessible. En Python, les appels imbriqués non résolus finissent par déclencher RecursionError ; les langages qui prennent correctement en charge les appels terminaux peuvent réutiliser l’espace de pile pour les appels en position finale.

Utiliser des fonctions ne suffit pas à rendre le code fonctionnel : les fonctions procédurales peuvent modifier l’état. Les programmes fonctionnels réels lisent toujours des entrées et produisent des sorties, en gardant généralement les effets près des frontières du programme et les calculs centraux purs.

Points clés

  • Les fonctions pures dépendent uniquement de leurs arguments et n’ont aucun effet de bord.
  • Les mises à jour immuables créent de nouvelles valeurs.
  • Les fonctions de première classe peuvent être transmises et renvoyées ; les fonctions d’ordre supérieur reçoivent ou renvoient des fonctions.
  • Le filtre sélectionne, la transformation modifie chaque élément et la réduction combine les éléments.
  • Une fermeture lexicale conserve son environnement de création.
  • Un calcul récursif fini nécessite un cas de base qu’il atteint effectivement.

5.2 Énonce un résultat et laisse un moteur le trouver

La programmation déclarative décrit le résultat voulu tout en laissant les étapes d’exécution à un moteur de langage. Le code fonctionnel est généralement regroupé sous cette idée générale, aux côtés des langages de requête et de la programmation logique.

Lire une requête comme une demande

Une base de données relationnelle stocke les données dans des tables. Supposons que students contienne les lignes de noms et de notes Ana 92, Ben 75, Chen 80 et Dara 64 :

SELECT name
FROM students
WHERE grade >= 80
ORDER BY name;

FROM indique la table, WHERE donne la condition, SELECT choisit le champ renvoyé et ORDER BY demande l’ordre alphabétique. Ana et Chen satisfont la condition, y compris avec la note de Chen, exactement égale à 80. Le résultat contient leurs noms dans cet ordre.

Un programme impératif créerait une liste de résultats, parcourrait les lignes, testerait chaque note, ajouterait les noms répondant au critère et trierait la liste. SQL indique à la base de données le résultat attendu sans écrire cette boucle.

Un optimiseur de requêtes choisit un plan d’exécution. Il peut parcourir la table ou utiliser un index disponible, une structure de recherche qui peut accélérer les recherches adaptées. De nombreux index utilisent des structures triées ; d’autres utilisent des organisations différentes. L’ajout d’un index peut modifier le plan sans modifier la requête. Davantage de données implique toujours du travail : déclarer le résultat ne garantit pas que chaque requête soit peu coûteuse.

Demander ce qui découle de faits et de règles

La programmation logique décrit des faits et des relations, puis recherche les valeurs qui répondent à une question. En Prolog :

parent(tom, bob).
parent(bob, ann).
grandparent(X, Z) :- parent(X, Y), parent(Y, Z).

La règle signifie que X est un grand-parent de Z lorsqu’il existe un Y pour lequel les deux relations de parenté sont vérifiées. La requête ?- grandparent(tom, Who). trouve Who = ann : Tom est le parent de Bob et Bob est le parent d’Ann. Les noms commençant par une majuscule représentent des variables ; les noms en minuscules désignent des personnes particulières.

Reconnaître les outils déclaratifs autour de toi

Une formule de tableur =SUM(B2:B5) énonce une dépendance. Le tableur la recalcule lorsque les cellules concernées changent. L’expression régulière [0-9]{3}-[0-9]{4} décrit trois chiffres, un trait d’union et quatre chiffres, comme dans « 555-0199 ». Un moteur de recherche de correspondances textuelles effectue la recherche.

CSS définit l’apparence avec une règle comme h1 { color: navy; } ; HTML balise la structure d’une page. Ce sont des notations déclaratives, généralement classées comme langages de style et de balisage. Leur rôle diffère de celui d’un langage généraliste qui peut exprimer une grande variété de calculs.

Type de langageTâche viséeExemples
Langage dédiéUn type particulier de travailSQL, expressions régulières, CSS
Langage généralisteDe nombreux types de programmesPython, Java, Haskell

Le code déclaratif peut rester proche de l’énoncé d’un problème et laisser un moteur améliorer sa méthode. Il donne aussi à la personne qui programme moins de contrôle direct sur chaque étape, ce qui peut rendre un plan lent plus difficile à diagnostiquer. Des instructions concrètes s’exécutent toujours en dessous : « déclaratif » décrit la façon dont la personne exprime la tâche, et non un processeur qui fonctionne sans étapes.

Points clés

  • Le code déclaratif énonce un résultat voulu ; un moteur choisit les étapes d’exécution.
  • SQL sélectionne des lignes et des champs à l’aide de conditions et d’un ordre demandé.
  • Un optimiseur peut modifier un plan de requête sans modifier la requête.
  • La programmation logique déduit des réponses à partir de faits et de règles.
  • Les langages dédiés visent des tâches particulières ; les langages généralistes prennent en charge de nombreux types de programmes.
6

Choisis un style et un langage adaptés à la tâche

Différents styles de programmation peuvent produire la même réponse tout en organisant le travail différemment. Les comparer sur une même tâche rend leurs compromis plus faciles à voir. Le choix d’un langage ajoute ensuite des contraintes pratiques : la plateforme cible, les bibliothèques disponibles, les limites de mémoire et de vitesse, les besoins de sûreté et les personnes qui maintiennent le code. Un projet composé de plusieurs parties peut utiliser pour chacune un langage différent et un mélange de styles différent.

6.1 Une tâche dans plusieurs styles de programmation

Un paradigme change la façon d’exprimer une solution, pas la réponse numérique qu’une solution correcte doit produire. Considère des commandes de montants [120, 45, 300, 80] : trouve le total des montants strictement supérieurs à 100.

Suivre les changements d’état

La version impérative rend visibles la boucle et l’accumulateur :

total = 0
for amount in amounts:
    if amount > 100:
        total = total + amount

Le premier montant fait passer total à 120. Le deuxième le laisse inchangé. Le troisième le fait passer à 420, et le quatrième le laisse à cette valeur. Le programme rend visibles l’ordre du test et celui de la mise à jour.

Transformer une collection

Une expression Python compacte de style fonctionnel est sum(a for a in amounts if a > 100). Elle sélectionne 120 et 300 et les combine pour obtenir 420. Une autre version est sum(filter(lambda a: a > 100, amounts)). Ici, le prédicat décrit quels éléments sont retenus et sum combine les valeurs sélectionnées.

Une expression génératrice ou un appel à filter utilise les fonctionnalités multiparadigmes de Python ; sa présence ne rend pas pures toutes les parties de l’application qui l’entourent.

Associer les règles aux objets

Dans une conception orientée objet, un objet Order stocke un amount et propose is_large(), qui renvoie self.amount > 100. Un objet OrderBook contient des objets représentant les commandes et propose large_total(). Cette méthode peut parcourir les commandes, demander à chacune si elle satisfait le critère et additionner les montants correspondants.

Le total reste 420. La différence tient à la répartition des responsabilités : la règle définissant une commande importante appartient à Order, et le travail portant sur la collection appartient à OrderBook. D’autres fonctionnalités peuvent réutiliser la règle sans copier son test de seuil.

Interroger la base de données

Si les données se trouvent déjà dans une base de données relationnelle, SQL peut demander le total :

SELECT SUM(amount)
FROM orders
WHERE amount > 100;

La base de données choisit le plan de récupération et d’agrégation. La requête ne demande pas à un appelant de parcourir chaque ligne ou de conserver un accumulateur local.

StyleCe que l’exemple met en avant
ImpératifTests et mises à jour ordonnés d’un total
Orienté objetObjets représentant les commandes et la collection, avec des responsabilités définies
FonctionnelSélection et combinaison de valeurs
SQL déclaratifRésultat requis à partir des données stockées
Combiner les styles lorsqu’ils sont utiles

Python prend en charge les techniques procédurales, orientées objet et fonctionnelles. JavaScript fournit des objets, des fonctions de première classe et des fermetures lexicales. C++, C#, Kotlin, Scala, Swift et Rust mélangent aussi les styles. Java est fortement associé à la programmation orientée objet, mais prend en charge les expressions lambda depuis Java 8 en 2014 ; Haskell est purement fonctionnel.

Une application web peut utiliser des classes pour les utilisateurs et les commandes, des transformations fonctionnelles pour les données des rapports, des mises à jour impératives dans les méthodes et SQL pour les requêtes de base de données. Ces choix interviennent à différentes couches.

La programmation événementielle ajoute une autre idée d’organisation : enregistrer un gestionnaire qui s’exécute lorsqu’un événement arrive. En JavaScript, button.addEventListener("click", showMenu) transmet showMenu comme une valeur de fonction. Un clic ultérieur l’appelle. Le gestionnaire peut lui-même utiliser des objets, des transformations pures ou des étapes impératives. Son rôle événementiel indique quand il s’exécute, pas le seul style que son corps peut utiliser.

Points clés

  • Des solutions correctes selon différents paradigmes peuvent calculer le même résultat.
  • Un style modifie l’endroit où apparaissent les étapes, l’état, les règles et les responsabilités.
  • Les langages modernes prennent généralement en charge plusieurs paradigmes.
  • Un gestionnaire d’événement s’exécute en réponse à un événement et peut utiliser d’autres styles en interne.

6.2 Choisis en fonction des contraintes et des compromis

Aucun langage de programmation ne convient mieux que tous les autres à chaque projet. Les langages généralistes sont généralement Turing-complets : avec un temps et une mémoire sans limites, ils peuvent exprimer la même classe de calculs. Dans la pratique, le choix dépend de l’adéquation de la solution à la plateforme, aux ressources, aux outils et aux personnes concernés.

Commencer par ce qui doit fonctionner

La plateforme est souvent une contrainte incontournable. Les navigateurs exécutent JavaScript et peuvent exécuter des modules compilés grâce à WebAssembly. TypeScript ajoute une vérification statique et se compile en JavaScript. Swift est un choix courant pour les applications des plateformes Apple ; Kotlin est recommandé pour Android. SQL est le langage habituel des requêtes relationnelles.

Considère ensuite le domaine et l’écosystème. Python possède des bibliothèques de données et d’apprentissage automatique largement utilisées, comme NumPy et pandas. R se concentre sur les statistiques. C et C++ sont courants dans les systèmes d’exploitation, les moteurs de jeux et le code embarqué. Java et C# sont courants dans les grands systèmes d’entreprise ; Bash est utile pour automatiser les tâches du système d’exploitation.

Ce sont des associations pratiques, pas des domaines réservés. Une bibliothèque adaptée peut économiser bien plus de travail de développement qu’une petite différence de syntaxe entre langages.

Examiner les besoins en ressources et en sûreté

Un microcontrôleur doté de quelques kilo-octets de mémoire vive seulement peut nécessiter un contrôle précis de la mémoire et du surcoût à l’exécution. C, C++ ou Rust peuvent convenir lorsque la cible les prend en charge. Les composants exigeants des jeux ou des bases de données favorisent souvent ces langages eux aussi, mais les moteurs et les budgets de ressources diffèrent : Unity utilise C#, et certaines bases de données utilisent Java ou Go.

Avant de choisir en fonction de la vitesse, trouve le goulot d’étranglement : la partie qui limite les performances. Un programme qui attend principalement une réponse du réseau ne deviendra pas beaucoup plus rapide simplement parce qu’une petite boucle locale est compilée plus efficacement. Mesure un travail représentatif sur la plateforme cible.

La sûreté mémoire réduit les erreurs comme les accès mémoire invalides. Le Rust sûr vérifie les règles de possession à la compilation ; les langages à mémoire gérée comme Java, C#, Go et Python assurent la gestion de la mémoire à l’exécution. Le typage statique détecte de nombreuses opérations incompatibles avant l’exécution, ce qui peut aider dans une grande base de code. Ni la sûreté mémoire ni le typage statique ne prouvent que la logique du programme est correcte.

Inclure le coût de maintenance du résultat

Les connaissances de l’équipe, les personnes disponibles pour le développement, les débogueurs, les outils de test, les bibliothèques, les licences et le code existant influent tous sur la décision. Un langage connu de l’équipe et pris en charge peut réduire les coûts de livraison et de maintenance. Réécrire un système fonctionnel entraîne du travail de migration et des risques, même si le nouveau langage possède des fonctionnalités attrayantes.

Pour un réseau météorologique urbain, les différents composants ont des besoins différents :

ComposantChoix de langage raisonnableRaison
Micrologiciel de petits capteursC ou RustContrôle de la mémoire et matériel cible pris en charge
Relevés stockésSQLFiltrage et agrégation dans la base de données
Nettoyage et analysePythonBibliothèques de traitement des données
Affichage interactif dans le navigateurJavaScript ou TypeScriptPrise en charge par le navigateur et outils d’interface

Une justification utile relie chaque choix à un besoin : « L’équipe maintient déjà le service Java et ses bibliothèques prennent en charge la nouvelle fonctionnalité. » Un rang dans un classement de popularité ne peut pas fournir ce raisonnement à lui seul. Les enquêtes peuvent indiquer l’existence d’une grande communauté ou d’un vaste vivier de recrutement ; elles n’établissent pas quel langage convient le mieux à une charge de travail particulière.

Points clés

  • Vérifie les contraintes de la plateforme avant de considérer les préférences.
  • Les bibliothèques et les outils relient un langage au domaine du problème.
  • Mesure le goulot d’étranglement avant de choisir principalement en fonction de la vitesse.
  • La sûreté mémoire et la vérification statique réduisent certains défauts, mais la logique doit toujours être testée.
  • Les compétences de l’équipe, le code existant et les coûts de maintenance entrent dans la décision.
  • Des composants différents peuvent justifier des langages différents.

Langages et paradigmes de programmation : exemples

6 Leçons Interactives

1

Suis un programme du code source à l’exécution

13 étapes
Suis les couches de traduction, examine une construction et compare les modes d’exécution.
  1. 1
    IntroductionUn calcul traverse plusieurs couches

    Tu écris un calcul, mais un processeur a besoin de petites instructions codées appelées code machine. Un traducteur fournit les détails manquants.

    Suis x = a + b depuis le code source vers les niveaux inférieurs, puis suis le programme lors de sa construction et de son exécution. Distingue deux questions : sous quelle forme se trouve le code et quel logiciel l’exécute ? Un compilateur, un interpréteur et un compilateur à la volée répondent différemment à la seconde. Cette distinction expliquera la portabilité et le moment où les erreurs apparaissent.

  2. 2
    ExempleUn calcul devient plusieurs actions

    Pour x = a + b, imagine quatre actions de niveau inférieur :

    1. Charger la valeur de a dans un registre.
    2. Charger la valeur de b dans un autre registre.
    3. Additionner les valeurs des deux registres.
    4. Stocker le résultat pour x.

    L’instruction de haut niveau masque le choix des registres et les détails du stockage. Les instructions réellement générées peuvent différer, car un compilateur peut optimiser le travail. Le calcul requis reste le même.

  3. 3
    ExplicationDes instructions lisibles ciblent toujours un processeur

    Le langage d’assemblage utilise des noms d’instructions comme mov et add. Un assembleur les code en instructions machine.

    Pour x86-64, mov eax, 1 place 1 dans EAX ; add eax, ebx y ajoute EBX. Ces registres et ces encodages appartiennent à cette architecture du jeu d’instructions. Remplacer les octets par des noms lisibles aide les humains, mais ne transforme pas les instructions en une description portable de haut niveau.

  4. 4
    DéfiTransfère le programme vers un autre processeur

    Une équipe possède un code source C portable et un exécutable natif x86-64. Elle a besoin d’un exécutable natif ARM. Quel élément peut-elle utiliser comme point de départ et quel travail est nécessaire ?

    ---reveal---

    Utilise le code source et compile-le pour la cible ARM, avec la prise en charge nécessaire de la plateforme. L’exécutable x86-64 contient des instructions destinées à une autre architecture du jeu d’instructions. La portabilité du code source permet une nouvelle traduction ; elle ne transforme pas les instructions machine existantes en code ARM natif.

  5. 5
    ExempleIdentifie les unités lexicales avant la structure

    Considère total = price * 3; en C.

    L’analyse lexicale trouve six unités lexicales : total, =, price, *, 3 et ;. L’analyse syntaxique regroupe ensuite price * 3 comme l’expression dont le résultat est affecté à total.

    Ce sont des tâches distinctes. L’analyse lexicale identifie les éléments ; l’analyse syntaxique établit comment ils s’assemblent. L’analyse sémantique peut ensuite vérifier, par exemple, si les noms sont déclarés et si les types des opérandes sont compatibles.

  6. 6
    ExempleDéplace le calcul des constantes dans la construction

    L’expression source 60 * 60 * 24 utilise uniquement des constantes. Le repliement des constantes peut l’évaluer avant l’exécution : d’abord 3 600, puis 86 400.

    La génération de code peut utiliser ce résultat au lieu d’instructions qui répètent les multiplications. C’est une optimisation, car elle change la façon dont le travail est effectué tout en préservant le résultat requis. Remplacer une valeur par une approximation simplement pratique ne serait pas justifié par cette règle.

  7. 7
    ExplicationRelie les fichiers traduits aux routines des bibliothèques

    La compilation peut produire des fichiers objets distincts. Un éditeur de liens relie ces fichiers et résout les références aux opérations des bibliothèques.

    L’édition de liens statique inclut le code des bibliothèques dans l’exécutable construit. L’édition de liens dynamique repose sur une bibliothèque partagée compatible chargée plus tard. Aucune des deux approches ne nécessite la présence du compilateur anticipé sur la machine de l’utilisateur, mais un exécutable lié dynamiquement a toujours besoin de ses dépendances à l’exécution.

  8. 8
    DéfiUne construction réussie donne une réponse incorrecte

    Un programme C est construit avec succès. Son rapport doit soustraire une quantité utilisée d’une quantité initiale, mais le code les additionne. Le compilateur a-t-il omis de vérifier une règle du langage ?

    ---reveal---

    Non. Une addition peut avoir une syntaxe valide et des types compatibles tout en exprimant une tâche incorrecte. C’est une erreur de logique. Le compilateur vérifie les règles du langage et génère du code ; un test dont le résultat attendu est connu est nécessaire pour révéler l’écart entre le calcul et son objectif.

  9. 9
    ExplicationUn environnement d’exécution peut exécuter des instructions intermédiaires

    CPython compile un fichier Python valide en bytecode, puis exécute ce bytecode dans sa machine virtuelle. Le processeur exécute les instructions machine de l’environnement d’exécution pendant que celui-ci réalise les opérations Python.

    Java utilise aussi du bytecode : javac produit des fichiers de classe pour une machine virtuelle Java compatible. Le bytecode appartient à un modèle d’instructions d’un environnement d’exécution ; il ne faut donc pas le confondre avec un jeu d’instructions universel pour processeur.

  10. 10
    ExempleCompare deux moments où l’exécution peut s’arrêter

    Dans ce fichier Python valide, la première ligne s’exécute :

    print("start")
    x = 10
    print(x + "5")
    

    Elle affiche « start » avant que la dernière ligne ne déclenche TypeError. Supprime maintenant la parenthèse fermante finale : CPython ne peut pas compiler le fichier, donc aucune instruction de ce fichier ne s’exécute. Une erreur de syntaxe pendant la préparation et une erreur de type pendant une opération exécutée arrêtent le programme à des étapes différentes.

  11. 11
    ExplicationCompile le code très utilisé pendant l’exécution

    Un compilateur à la volée produit du code machine pendant l’exécution du programme. Un environnement d’exécution peut d’abord interpréter le code, observer les méthodes fréquemment utilisées et compiler ces méthodes.

    Cela déplace les coûts : la compilation utilise du temps et de la mémoire pendant l’exécution, tandis que les exécutions répétées peuvent devenir plus rapides ensuite. Un service de longue durée peut bénéficier davantage de cette mise en régime qu’un petit script exécuté une seule fois. Le résultat dépend de l’implémentation et de la charge de travail.

  12. 12
    DéfiDistingue la portabilité du bytecode de la vitesse du code natif

    Un fichier de classe Java s’exécute au moyen d’une machine virtuelle Java compatible sur deux architectures de jeux d’instructions différentes. À chaque exécution, l’environnement compile une méthode fréquemment utilisée. Pourquoi un même fichier de classe peut-il être partagé alors que le code machine généré diffère ?

    ---reveal---

    Le fichier de classe contient du bytecode de machine virtuelle Java, que les deux environnements compatibles comprennent. Chaque compilateur à la volée cible son propre processeur hôte. La portabilité vient du format intermédiaire partagé et des environnements compatibles ; les instructions machine produites pendant l’exécution restent propres à chaque plateforme.

  13. 13
    RécapitulatifDistingue les niveaux et les modes d’exécution

    Le code machine cible une architecture du jeu d’instructions ; le langage d’assemblage donne des noms lisibles à ses instructions ; le code source de haut niveau masque davantage de détails du matériel.

    Un compilateur traduit le code, un interpréteur l’exécute et un compilateur à la volée compile pendant l’exécution. Un environnement d’exécution peut combiner ces méthodes. Pour expliquer un système réel, identifie ses formes source, intermédiaire et native, puis indique quelle étape vérifie la syntaxe et quelle étape exécute l’opération susceptible d’échouer.

2

Suis le sens d’un court programme

10 étapes
Lis la grammaire, l’ordre d’évaluation, les changements d’état et les limites des fonctions.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionLa forme et le sens répondent à des questions différentes
  2. 2
    ExempleUn code valide peut regrouper le mauvais calcul
  3. 3
    ExplicationLa première condition peut protéger la seconde
  4. 4
    DéfiPrévois si la division s’exécute
  5. 5
    ExplicationUne affectation lit avant de mettre à jour
  6. 6
    ExempleSuis chaque mise à jour de l’accumulateur
  7. 7
    ExempleSuis un appel et sa valeur de retour
  8. 8
    DéfiUne mise à jour locale peut laisser l’appelant inchangé
  9. 9
    DéfiSuis ensemble la grammaire et l’état
  10. 10
    RécapitulatifLis le chemin exécuté
3

Suis les types et les objets partagés

14 étapes
Compare le moment des vérifications, suis les références partagées et détermine quand la mémoire peut être libérée.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionSuis les valeurs, les objets et leur stockage
  2. 2
    ExplicationDes valeurs typées peuvent être associées à différents noms
  3. 3
    Point cléL’inférence et la conversion répondent à des questions différentes
  4. 4
    DéfiLis les chiffres comme la valeur réellement renvoyée
  5. 5
    ExplicationLa représentation influe sur les résultats numériques
  6. 6
    ExplicationUn bloc d’activation peut disparaître avant un objet
  7. 7
    ExempleDeux noms pointent vers une seule liste
  8. 8
    DéfiUn paramètre peut modifier un objet puis désigner autre chose
  9. 9
    ExplicationIdentifie précisément comment l’espace mémoire est mal utilisé
  10. 10
    ExempleSuis les références jusqu’à la disparition de la dernière
  11. 11
    Point cléUn cycle peut ne plus avoir de point d’entrée actif
  12. 12
    ExplicationLa possession vérifie les accès avant l’exécution
  13. 13
    DéfiTrouve pourquoi le ramassage ne peut pas récupérer une liste
  14. 14
    RécapitulatifSuis trois décisions distinctes
4

Protège les règles et choisis les relations entre objets

12 étapes
Suis des commandes dont l’ordre compte, puis utilise l’encapsulation, la redéfinition de méthode et la composition.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionLes étapes ont besoin de frontières fiables
  2. 2
    ExempleL’ordre des opérations modifie l’état partagé
  3. 3
    ExplicationRends utile la délimitation d’une procédure
  4. 4
    DéfiPrévois une mise à jour dans un ordre différent
  5. 5
    ExplicationDonne à chaque compte son propre état
  6. 6
    ExempleVérifie avant de modifier un solde
  7. 7
    DéfiRepère une règle contournée
  8. 8
    ExempleSpécialise sans rompre le contrat
  9. 9
    ExplicationLaisse l’objet choisir sa méthode de calcul d’aire
  10. 10
    LienContiens le composant qui effectue le travail
  11. 11
    DéfiChoisis deux relations dans une nouvelle conception
  12. 12
    RécapitulatifFais correspondre les frontières aux responsabilités
5

Transforme une collection et interroge un résultat

13 étapes
Suis une chaîne de traitement fonctionnelle et une requête déclarative, en examinant les effets et l’exécution.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionÉnonce une transformation ou une demande
  2. 2
    ExplicationLa pureté décrit plus que le nombre renvoyé
  3. 3
    ExempleSélectionne d’abord les valeurs paires
  4. 4
    ExempleTransforme et combine les valeurs sélectionnées
  5. 5
    DéfiApplique la chaîne de traitement à un nouveau cas
  6. 6
    ExplicationRenvoie un comportement qui conserve son facteur
  7. 7
    ExempleRéduis le problème jusqu’à atteindre un cas direct
  8. 8
    ExempleLis SQL comme une demande portant sur des lignes
  9. 9
    Point cléConserve le résultat en changeant le plan
  10. 10
    ExempleRelie deux faits par une règle
  11. 11
    DéfiLis une nouvelle requête en tenant compte de sa valeur limite
  12. 12
    DéfiCombine une requête et une transformation pure
  13. 13
    RécapitulatifGarde visible la relation entre l’entrée et le résultat
6

Compare les styles et justifie un choix de langage

10 étapes
Garde la même tâche, puis choisis des outils pour des composants soumis à des contraintes différentes.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionGarde la même tâche
  2. 2
    ExempleGarde la même tâche en changeant de style
  3. 3
    ExemplePlace la même règle dans des objets ou une requête
  4. 4
    DéfiDétermine si une conception utilise un seul style
  5. 5
    ExplicationÉcarte d’abord les cibles non prises en charge
  6. 6
    ExempleChoisis pour un système météorologique complet
  7. 7
    ConseilMesure ce qui prend du temps
  8. 8
    DéfiDéfends un choix face à des contraintes concurrentes
  9. 9
    DéfiRelie le choix du langage à l’organisation du programme
  10. 10
    RécapitulatifDonne une raison liée au besoin

Langages et paradigmes de programmation : QCM

50 Questions Pratiques en 5 séries

Série 1 : Lis du code et identifie le mode d’exécution10 Questions Pratiques

Question 1 sur 10

Une personne remplace les octets bruts d’instructions par des noms comme mov et jmp. À quel niveau de langage le programme est-il maintenant écrit ?
  1. En code machine
  2. En langage d’assemblage
  3. Dans un langage de haut niveau
  4. Dans un langage de requête de base de données
Afficher la réponseMasquer la réponse
Réponse : B
Le langage d’assemblage écrit les opérations du processeur à l’aide de mnémoniques et de noms de registres, ce qui les rend plus faciles à lire. Il reste lié à une architecture du jeu d’instructions. Les noms lisibles ne suffisent pas à en faire un langage de haut niveau, car la personne qui programme précise toujours les opérations individuelles de bas niveau.

Question 2 sur 10

Pendant une construction, un outil relie plusieurs fichiers objets et résout un appel à une routine de bibliothèque. Quel outil effectue cette tâche ?
  1. Un assembleur
  2. Un analyseur syntaxique
  3. Un interpréteur
  4. Un éditeur de liens
Afficher la réponseMasquer la réponse
Réponse : D
Un éditeur de liens relie les parties traduites et résout les références au code défini ailleurs, notamment aux routines des bibliothèques. Un compilateur peut avoir produit les fichiers objets plus tôt dans la construction. L’analyse syntaxique vérifie la structure du code source ; elle ne relie pas les fichiers obtenus pour former un programme complet.

Question 3 sur 10

Un fichier Python contient une instruction d’affichage sur la première ligne et une parenthèse non fermée sur la dernière. Que se passe-t-il lorsque CPython exécute le fichier ?
  1. Il signale une erreur de syntaxe avant l’affichage.
  2. Il affiche la première ligne puis signale une erreur de syntaxe.
  3. Il affiche la première ligne puis déclenche une erreur d’exécution.
  4. Il ignore uniquement la dernière ligne et exécute les lignes valides.
Afficher la réponseMasquer la réponse
Réponse : A
CPython doit compiler le fichier avant d’exécuter ses instructions, et la parenthèse non fermée empêche cette préparation. Le premier affichage ne s’exécute donc jamais. Un affichage avant l’échec correspondrait à une erreur d’exécution dans un code valide, mais ce fichier comporte une erreur de syntaxe détectée plus tôt.

Question 4 sur 10

Un programme Python valide affiche une moyenne incorrecte parce qu’il ajoute une valeur après avoir divisé l’autre par deux. De quel type d’erreur s’agit-il ?
  1. Une erreur de syntaxe
  2. Une erreur d’exécution
  3. Une erreur de logique
  4. Une erreur d’incompatibilité de types
Afficher la réponseMasquer la réponse
Réponse : C
Les opérations se terminent, mais leur sens ne correspond pas à la moyenne attendue. Il s’agit donc d’une erreur de logique. Une erreur de syntaxe enfreindrait la grammaire du langage, tandis qu’une erreur d’exécution impliquerait l’échec d’une opération exécutée ; aucune des deux n’est indiquée dans ce calcul qui se termine mais donne un résultat incorrect.

Question 5 sur 10

Quelle valeur finale ce code Python stocke-t-il dans total ?

total = 1
for n in [4, 7, 2]:
    total = total + n
  1. 13
  2. 3
  3. 15
  4. 14
Afficher la réponseMasquer la réponse
Réponse : D
Le total commence à 1, puis devient 5 après l’ajout de 4, 12 après l’ajout de 7 et 14 après l’ajout de 2. La boucle conserve le résultat de chaque mise à jour précédente. Obtenir 13 reviendrait à ignorer la valeur initiale et à calculer uniquement la somme de la liste, ce qui ne correspond pas au calcul complet écrit ici.

Question 6 sur 10

En Python, label = "18" est suivi de label = 18. Le nom peut changer de type, mais "18" + 2 déclenche une erreur. Comment décrit-on ces deux faits ?
  1. Typage statique avec des règles strictes pour l’addition de types différents
  2. Typage dynamique avec des règles strictes pour l’addition de types différents
  3. Typage dynamique avec addition numérique automatique
  4. Typage statique avec addition numérique automatique
Afficher la réponseMasquer la réponse
Réponse : B
Réassocier un nom d’un texte à un entier est compatible avec le typage dynamique, tandis que l’échec de l’addition montre que cette opération ne convertit pas automatiquement le texte en nombre. Le moment de la vérification et le comportement des conversions sont des questions distinctes. Le typage dynamique ne signifie pas que des types incompatibles sont automatiquement combinés.

Question 7 sur 10

Que contient first après l’exécution de ce code Python ?

first = [4, 8]
second = first
second.append(6)
  1. [4, 8]
  2. [6]
  3. [4, 8, 6]
  4. [4, 8, [6]]
Afficher la réponseMasquer la réponse
Réponse : C
Les deux noms font référence à une seule liste, car affecter la référence ne copie pas son contenu. L’ajout par l’intermédiaire de second modifie cette liste partagée, donc first montre aussi le 6 ajouté. Conserver [4, 8] nécessiterait une liste distincte, par exemple créée par une copie avant l’ajout.

Question 8 sur 10

Un programme de rapport est divisé en read_data(), calculate_total() et save_report(), chacun contenant des commandes ordonnées. Quelle organisation cela illustre-t-il ?
  1. La programmation procédurale
  2. La programmation logique
  3. La programmation fonctionnelle pure
  4. L’héritage de classes
Afficher la réponseMasquer la réponse
Réponse : A
La programmation procédurale regroupe les étapes impératives en tâches nommées, ce qui correspond exactement à l’organisation de ces opérations de rapport. Les noms des fonctions ne suffisent pas à établir qu’il s’agit de programmation fonctionnelle pure. En particulier, la lecture et l’enregistrement sont des effets, et rien dans la description n’exige que les calculs évitent les modifications partagées.

Question 9 sur 10

Une fonction Python récursive renvoie 0 lorsque n == 0 ; sinon, elle s’appelle elle-même avec n + 1. En partant de la valeur positive 2, pourquoi le cas de base écrit ne permet-il pas au calcul de se terminer ?
  1. Les entrées atteignent zéro, mais le retour de zéro relance la récursivité.
  2. Le cas de base garantit la terminaison même pour des entrées croissantes.
  3. La récursivité se déroule vers zéro avant de vérifier le cas de base.
  4. Les entrées récursives s’éloignent de zéro au lieu de l’atteindre.
Afficher la réponseMasquer la réponse
Réponse : D
Les entrées successives sont 2, 3, 4, et ainsi de suite : aucune ne satisfait donc le test d’arrêt. Un cas de base doit être effectivement atteint par la progression récursive ; sa seule présence dans le code source ne suffit pas. Renvoyer 0 dans cette branche jamais atteinte ne peut pas modifier les arguments fournis par les appels qui s’exécutent.

Question 10 sur 10

Une requête de tableau de bord effectue deux étapes successives : 180 ms d’attente des données réseau, puis 20 ms pour leur mise en forme locale. Un nouveau langage réduirait la mise en forme à 5 ms sans modifier l’attente réseau. Quelle est la meilleure évaluation de ce seul changement ?
  1. Il économise 15 ms, tandis que le réseau reste la principale attente.
  2. Il économise 150 ms en appliquant l’accélération par quatre à la requête.
  3. Il économise 5 ms, car le nouveau temps de mise en forme est le gain.
  4. Il économise 20 ms, car la compilation supprime la mise en forme.
Afficher la réponseMasquer la réponse
Réponse : A
Le travail séquentiel indiqué passe de 200 ms à 185 ms, soit un gain de 15 ms. L’attente réseau de 180 ms reste inchangée et domine toujours le temps de la requête. Appliquer l’accélération par quatre de la mise en forme à toute la requête reviendrait à supposer à tort que la même amélioration touche aussi l’attente réseau.
Série 2 : Applique les règles des types, des objets et des requêtes10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Une équipe peut compiler le même code source portable pour deux architectures de jeux d’instructions différentes. Quelle relation entre ce code source et les résultats natifs est correcte ?
  1. Le code source est commun ; les deux résultats ciblent la même architecture.
  2. Le code source doit différer ; chaque résultat cible sa propre architecture.
  3. Le code source est commun ; chaque résultat cible sa propre architecture.
  4. Le code source doit différer ; les deux résultats ciblent la même architecture.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Un environnement d’exécution traduit une méthode fréquemment exécutée en instructions natives après le démarrage de l’application. De quelle technique s’agit-il ?
  1. La compilation à la volée
  2. La compilation anticipée
  3. L’édition de liens statique
  4. L’analyse lexicale
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Avec a = 8 et b = 12 en Python, quelles valeurs a + b / 2 et (a + b) / 2 produisent-elles respectivement ?
  1. 10,0 et 10,0
  2. 14,0 et 14,0
  3. 10,0 et 14,0
  4. 14,0 et 10,0
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Une fonction C libère un bloc alloué, mais une partie ultérieure du code le lit au moyen d’un ancien pointeur. Quelle erreur cette lecture provoque-t-elle ?
  1. Une fuite mémoire
  2. Une utilisation après libération
  3. Une double libération
  4. Un débordement de tampon
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Une classe définit un attribut balance et une méthode deposit. Deux comptes sont créés avec des soldes initiaux différents. Où ces soldes distincts se trouvent-ils ?
  1. Dans la définition partagée du dépôt
  2. Dans les deux instances de compte
  3. Dans le nom de la classe elle-même
  4. Dans les noms des paramètres du constructeur
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Un objet représentant un vélo stocke un objet représentant un système de freinage et lui demande d’arrêter les roues. Quelle relation correspond le mieux à cette conception ?
  1. Composition : le vélo possède un système de freinage.
  2. Héritage : le vélo est un système de freinage.
  3. Redéfinition : le vélo remplace le système de freinage.
  4. Typage canard : le vélo est une sous-classe de système de freinage.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

À partir de [1, 4, 7, 10], une chaîne de traitement conserve les valeurs supérieures à 3, soustrait 1 à chaque valeur retenue, puis les additionne. Quel est le résultat ?
  1. 21
  2. 17
  3. 18
  4. 22
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Une base de données peut exécuter une requête SQL inchangée en parcourant les lignes ou en utilisant un index disponible. Qu’est-ce qui choisit entre ces plans ?
  1. L’analyseur syntaxique de requêtes SQL
  2. L’éditeur de liens de la base de données
  3. Le vérificateur de types SQL
  4. L’optimiseur de requêtes
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Une méthode de classe Python utilise une boucle pour mettre à jour une liste et une fonction pure pour transformer chaque élément. Comment décrire ses styles ?
  1. Une organisation objet qui exclut le travail impératif
  2. Une organisation fonctionnelle qui rend toute méthode pure
  3. Une organisation objet avec du travail impératif et fonctionnel
  4. Une organisation impérative qui empêche les méthodes d’objet
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Que valent x et y après ce code Python ?

x = [12]
y = x
x = x + [3]
y.append(6)
  1. x vaut [12, 3] ; y vaut [12, 6].
  2. x vaut [12, 3, 6] ; y vaut [12, 3, 6].
  3. x vaut [12, 3, 6] ; y vaut [12].
  4. x vaut [12, 6] ; y vaut [12, 3].
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Série 3 : Suis les appels, les effets et les contrats des objets10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Un compilateur remplace minutes = 60 * 12 par la valeur précalculée 720. Quelle optimisation est utilisée et que doit-elle préserver ?
  1. L’édition de liens dynamique, qui préserve les noms des fichiers de bibliothèque
  2. L’analyse lexicale, qui préserve chaque caractère du code source
  3. La mise en régime de la compilation à la volée, qui préserve les nombres d’appels à l’exécution
  4. Le repliement des constantes, qui préserve le comportement requis
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Qu’affiche ce programme Python ?

score = 11
def change():
    score = 4
    return score + 2
print(change())
print(score)
  1. 6, puis 4
  2. 6, puis 11
  3. 4, puis 11
  4. 13, puis 11
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Que vaut items après l’appel ?

items = [3]
def revise(values):
    values.append(8)
    values = [1]
    values.append(5)
revise(items)
  1. [3, 8]
  2. [1, 5]
  3. [3, 8, 5]
  4. [3]
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Deux objets font référence l’un à l’autre. Toutes les références provenant des racines actives du programme vers ces objets sont supprimées. Pourquoi le comptage de références seul peut-il les manquer, alors que le traçage peut les récupérer ?
  1. Les compteurs internes atteignent zéro : le traçage doit donc libérer les objets à compteur nul.
  2. Les compteurs internes restent positifs : les deux objets doivent donc rester accessibles.
  3. Les compteurs internes restent positifs, mais aucun des deux objets n’est accessible.
  4. Les compteurs internes égalent le nombre de racines : les deux objets ont donc un compteur nul.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

En partant de 120, une opération applique une remise de 25 % et une autre ajoute 8. Quelles valeurs finales obtient-on en appliquant la remise puis l’ajout, et en effectuant l’ajout puis la remise ?
  1. 96 et 98
  2. 98 et 98
  3. 98 et 96
  4. 96 et 96
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Une méthode de compte vérifie le montant d’un retrait avant de modifier le solde enregistré. Quel principe de conception place cette règle à côté de la mise à jour qu’elle contrôle ?
  1. L’encapsulation
  2. L’héritage
  3. Le repliement des constantes
  4. L’inférence de types
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Un compte spécialisé facture des frais de 2 pour chaque retrait réussi et ne doit jamais avoir un solde négatif. Son solde est de 43. Que doit-il se passer pour une demande de retrait de 42 ?
  1. Accepter la demande et laisser le solde à 1.
  2. Accepter la demande et laisser le solde à −1.
  3. Refuser la demande après avoir réduit le solde à 1.
  4. Refuser la demande et conserver le solde à 43.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Une fonction renvoie le résultat d’un calcul fixe à partir de ses arguments et affiche aussi un message dans le journal. Est-elle pure, et pourquoi ?
  1. Oui ; son résultat est prévisible.
  2. Non ; l’affichage est un effet de bord.
  3. Oui ; le journal n’est pas une donnée stockée.
  4. Non ; tout code a des effets de bord.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Une page web enregistre showDetails pour un événement de clic. Pourquoi ce même gestionnaire peut-il toujours utiliser des méthodes d’objets et des affectations impératives ?
  1. Les événements le rendent orienté objet seulement s’il modifie des attributs.
  2. Les événements déterminent son exécution ; son corps peut utiliser d’autres styles.
  3. Les événements exigent des fonctions de rappel pures, donc les affectations doivent être déplacées.
  4. Les événements le rendent déclaratif, donc le moteur choisit ses mises à jour.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Une nouvelle fonctionnalité d’analyse doit utiliser une bibliothèque reconnue disponible uniquement en Python, et l’équipe maintient déjà des services Python qui respectent le budget de ressources requis. Quels faits indiqués justifient le choix de Python ?
  1. La vitesse brute des boucles et l’absence de coût mémoire à l’exécution
  2. La compatibilité avec les navigateurs et les annotations Python imposées
  3. Les calculs exacts en virgule flottante et toutes les cibles matérielles
  4. La prise en charge de la bibliothèque requise et l’expérience de l’équipe
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Série 4 : Raisonne à travers les frontières de l’exécution et de la conception10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Une instruction de haut niveau devient un nombre différent d’instructions dans deux versions optimisées, mais les deux respectent le même comportement du langage. Cette différence de nombre rend-elle l’une des versions incorrecte ?
  1. Non ; une instruction n’a pas à correspondre à un nombre fixe d’instructions.
  2. Oui ; chaque instruction doit correspondre à une instruction machine.
  3. Oui ; la portabilité du haut niveau exige un résultat natif identique.
  4. Non ; un résultat optimisé peut modifier les résultats requis.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Un exécutable natif utilise l’édition de liens dynamique. Son destinataire n’a pas de compilateur et ne possède pas une bibliothèque partagée compatible nécessaire. Quelle absence indiquée explique pourquoi l’exécutable peut ne pas démarrer ?
  1. Le compilateur anticipé est absent.
  2. Le fichier source d’origine est absent.
  3. La bibliothèque partagée nécessaire est absente.
  4. L’arbre d’analyse syntaxique du compilateur est absent.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Une machine virtuelle Java compatible exécute le même fichier de classe sur deux architectures de jeux d’instructions et compile à la volée sa méthode la plus utilisée sur chacune. Un membre de l’équipe affirme que cela prouve que le fichier de classe contient déjà des instructions natives pour les deux processeurs. Quel diagnostic est correct ?
  1. Le fichier contient du code natif ; la machine virtuelle ne fait que lier les bibliothèques de la plateforme.
  2. Le fichier contient du bytecode ; le résultat compilé à la volée est lui aussi indépendant de l’architecture.
  3. Le fichier contient du texte source ; la machine virtuelle doit recompiler le code source d’origine.
  4. Le fichier contient du bytecode ; chaque compilateur à la volée génère son propre code natif.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Avec n = 0 en Python, compare n != 0 and 36 / n > 4 et 36 / n > 4 and n != 0. Que se passe-t-il ?
  1. Les deux sont fausses, car elles contiennent les deux mêmes tests.
  2. La première est fausse ; la seconde déclenche une erreur de division.
  3. Les deux déclenchent une erreur, car elles mentionnent toutes deux une division.
  4. La première déclenche une erreur ; la seconde est fausse.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Quelle valeur finale est stockée dans total ?

def bump(x):
    return x + 2
total = 0
for n in [2, 5, 8]:
    if n > 3:
        total = total + bump(n)
  1. 17
  2. 15
  3. 19
  4. 21
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Un compilateur déduit tous les types des variables locales avant l’exécution, même si la personne qui programme n’écrit aucune annotation de type. Quelle approche de vérification cela illustre-t-il ?
  1. Le typage dynamique avec inférence à l’exécution
  2. Le typage faible sans information sur les types
  3. Le typage dynamique avec conversion explicite
  4. Le typage statique avec inférence de types
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Un langage à typage statique déduit un type entier à partir d’une valeur d’initialisation et autorise une conversion numérique implicite particulière. Que peut-on conclure de ces deux caractéristiques ?
  1. L’inférence rend ces variables dynamiques malgré la vérification statique.
  2. La conversion implicite signifie que la variable inférée n’a pas de type fixe.
  3. L’inférence et la conversion peuvent coexister avec une vérification statique.
  4. La vérification statique impose d’écrire explicitement la conversion.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Un programme de rapport parcourt des objets dans une boucle et appelle item.render(). Une nouvelle classe sans lien avec les autres fournit une méthode render() compatible. L’appelant Python n’exige aucune superclasse. Pourquoi l’objet peut-il rejoindre la boucle sans branche propre à son type ?
  1. Un héritage commun est nécessaire avant que sa méthode puisse s’exécuter.
  2. Le typage canard accepte l’objet ; la liaison dynamique choisit sa méthode.
  3. Des noms de méthodes identiques imposent un même corps à tous les objets.
  4. L’appelant doit sélectionner la classe concrète avant l’appel.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Une fonction de création renvoie une fonction qui multiplie son entrée par le facteur de la fonction de création. Le facteur était 4 et la fonction renvoyée est ensuite appelée avec 7. Quels résultat et mécanisme expliquent cela ?
  1. 28, grâce à un bloc d’activation
  2. 11, grâce à une fermeture lexicale
  3. 28, grâce à une fermeture lexicale
  4. 28, grâce à une conversion de type
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Une équipe considère qu’un classement de popularité suffit à prouver qu’un langage peut s’exécuter sur sa cible de déploiement requise. Quelle vérification de compatibilité manque-t-il ?
  1. Vérifier si la plateforme cible prend en charge l’environnement d’exécution nécessaire
  2. Vérifier si les tutoriels utilisent l’indentation préférée de l’équipe
  3. Vérifier si son nom figure en haut d’une autre enquête de popularité
  4. Vérifier si son historique de versions est plus court que celui des autres langages
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Série 5 : Diagnostique les limites et combine les idées10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Une implémentation fondée sur un interpréteur passe du temps à exécuter du bytecode. Une seconde implémentation du même langage ajoute un compilateur à la volée et devient plus rapide sur un long calcul répété. Quelle conclusion peut-on tirer sans modifier la définition du langage ?
  1. Le langage est devenu à typage statique parce qu’il a gagné un compilateur.
  2. La stratégie d’exécution a changé tandis que la sémantique du langage peut rester identique.
  3. Le premier environnement exécutait directement le code source, donc le bytecode n’a pas été compilé.
  4. Le langage commun doit modifier le sens de ses opérateurs pour gagner en vitesse.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Qu’affiche ce programme Python ?

value = 6
def step(value):
    value = value + 3
    return value
for n in [1, 2]:
    result = step(value)
print(value, result)
  1. 12 12
  2. 6 12
  3. 6 9
  4. 9 9
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Un service utilisant le ramassage des miettes stocke chaque enregistrement traité dans une liste globale qui reste accessible. Supprimer les variables locales ne réduit pas la mémoire occupée par les enregistrements conservés. Quel changement traite la cause indiquée ?
  1. Supprimer de la liste globale les références aux enregistrements devenus inutiles.
  2. Lancer le ramassage à plusieurs reprises sans modifier l’archive.
  3. Supprimer chaque association locale en conservant les entrées de l’archive globale.
  4. Remplacer la liste d’archive par une copie contenant les mêmes enregistrements.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Deux procédures mettent à jour la même valeur globale. double() la multiplie par 2 ; add_three() ajoute 3. En partant de la même valeur, quelle est toujours la différence entre doubler puis ajouter et ajouter puis doubler ?
  1. Doubler puis ajouter donne un résultat supérieur de 3.
  2. Ajouter puis doubler donne un résultat supérieur de 6.
  3. Les deux ordres donnent toujours le même résultat.
  4. Ajouter puis doubler donne un résultat supérieur de 3.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Une classe Java marque son champ de solde comme privé et expose une méthode de retrait qui effectue une validation. Du code situé dans une classe extérieure sans lien tente d’affecter directement un solde négatif. Qu’est-ce qui protège la règle de la classe à ce point d’accès ?
  1. La méthode de retrait valide les affectations directes.
  2. L’affectation crée un solde local distinct.
  3. L’accès privé autorise les écritures mais bloque toutes les lectures.
  4. Le compilateur rejette l’accès au champ privé.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Une classe hérite d’une file uniquement pour réutiliser son stockage, mais désactive l’opération de retrait de la file. Les appels qui s’attendent à une file utilisable échouent. Quelle nouvelle conception traite le problème de relation indiqué ?
  1. Conserver l’héritage de la file et masquer le retrait dans l’interface de la sous-classe.
  2. Contenir une file en interne et exposer les opérations promises par la classe.
  3. Conserver l’héritage de la file et faire en sorte que le retrait ne fasse rien sans le signaler.
  4. Conserver l’héritage de la file et documenter que les appelants doivent éviter le retrait.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Une table readings contient les lignes (level, active) : (50, true), (65, true), (20, false), (85, true) et (90, false). Une requête demande SUM(level) lorsque active = true AND level > 50. Après l’ajout d’un index adapté, quel total est requis et le plan d’exécution peut-il changer ?
  1. 150 ; le plan peut changer tout en préservant le total.
  2. 200 ; le plan peut changer tout en préservant le total.
  3. 240 ; le plan peut changer tout en préservant le total.
  4. 150 ; le nouvel index peut changer le plan et le total.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Prolog possède parent(lea, noah). et parent(noah, sora)., ainsi que la règle grandparent(X, Z) :- parent(X, Y), parent(Y, Z).. Que trouve grandparent(lea, Who) ?
  1. Who = noah
  2. Who = lea
  3. Who = sora
  4. À la fois Who = lea et Who = noah
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Une boucle impérative, une méthode d’objet et une requête SQL doivent calculer le total des montants strictement supérieurs à 50 dans [50, 65, 20, 85]. Quels doivent être leurs résultats corrects ?
  1. 200 dans les trois versions
  2. 150 dans les trois versions
  3. 150 pour la boucle, 200 pour la méthode et la requête
  4. 200 pour la boucle, 150 pour la méthode et la requête
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Une fonction renvoie une liste nouvellement allouée et son appelant stocke cette liste pour l’utiliser plus tard. La liste doit-elle disparaître lorsque le bloc d’activation de la fonction est retiré ?
  1. Oui ; la fin de l’appel annule la référence de l’appelant.
  2. Oui ; le retour copie son contenu et la libère.
  3. Non ; le bloc qui l’a créée doit rester actif pour la conserver.
  4. Non ; l’appelant conserve une référence à la liste.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Tu bloques sur ton propre exercice ? Aide aux Devoirs

Mots-Clés

94 Mots-Clés, chacun avec sa définition

A

Abstraction
L’abstraction masque les détails d’une implémentation derrière un modèle ou une interface plus simple afin de permettre à la personne qui programme de se concentrer sur la tâche exprimée.
Une variable est une abstraction qui permet au code de nommer une valeur sans préciser son adresse physique en mémoire.
Affectation
Une affectation associe un nom ou une cible de stockage à une valeur, en utilisant d’abord l’état actuel pour évaluer l’expression de droite.
L’affectation count = count + 1 lit l’ancien compteur avant de le remplacer par la valeur augmentée.
Aliasing
L’aliasing se produit lorsque différents noms ou références mènent au même objet, rendant ses mutations visibles par chacun d’eux.
Après b = a, l’aliasing explique pourquoi un ajout par b peut aussi modifier ce que l’on voit par a.
Allocation mémoire
L’allocation mémoire réserve de la mémoire pour les données d’un programme ; l’espace réservé ne peut pas être réutilisé avant sa libération.
La création d’une collection qui grandit peut nécessiter une allocation mémoire supplémentaire à mesure qu’elle stocke de nouveaux éléments.
Analyse lexicale
L’analyse lexicale découpe le texte source en unités lexicales porteuses de sens, notamment les identifiants, les littéraux, les opérateurs et la ponctuation.
Pendant l’analyse lexicale, price * 3 est séparé en un nom, un opérateur de multiplication et un nombre.
Analyse syntaxique
L’analyse syntaxique vérifie les unités lexicales par rapport à la grammaire d’un langage et représente la structure des expressions et des instructions.
L’analyse syntaxique indique que la multiplication dans total = price * 3; fait partie de l’affectation.
Architecture du jeu d’instructions
Une architecture du jeu d’instructions définit les opérations prises en charge par une famille de processeurs et les encodages utilisés pour les représenter.
Un exécutable x86-64 cible une architecture du jeu d’instructions différente de celle du code machine ARM natif.
Assembleur
Un assembleur est un traducteur qui code les instructions en langage d’assemblage en code machine pour un jeu d’instructions précis.
Un assembleur transforme add eax, ebx en octets d’instruction x86-64 correspondants.
Attribut
Un attribut est une donnée nommée ou un membre associé à un objet ou à une classe, auquel on accède par cet objet ou cette classe.
L’attribut de solde d’un compte indique son état actuel séparément du solde d’un autre compte.

B

Bloc d’activation
Un bloc d’activation stocke les informations associées à un appel de fonction actif, comme les associations locales et l’emplacement de retour.
Chaque appel récursif non terminé a besoin de son propre bloc d’activation, sauf si une implémentation peut l’éliminer par optimisation.
Bytecode
Le bytecode est une forme intermédiaire d’instructions conçue pour un environnement d’exécution logiciel plutôt que pour une exécution directe par le processeur hôte.
CPython génère du bytecode à partir d’un fichier Python valide avant que sa machine virtuelle exécute le programme.

C

Cas de base
Un cas de base donne directement un résultat récursif, permettant à un calcul fini de cesser de produire de nouveaux appels récursifs.
Le cas de base de la factorielle renvoie 1 pour zéro afin que les appels de multiplication en attente puissent se terminer.
Classe
Une classe définit la structure et le comportement de ses instances en décrivant leurs attributs et les méthodes disponibles.
La classe Loan d’une bibliothèque peut définir l’adhérent, le livre, la date de retour et l’opération de renouvellement pour chaque prêt.
Code machine
Le code machine est une suite d’instructions de processeur codées en binaire qui suivent une architecture du jeu d’instructions particulière.
Le processeur exécute le code machine après qu’un compilateur ou un assembleur a produit des instructions compatibles.
Compilateur
Un compilateur traduit le code source en une représentation cible, qui peut être constituée d’instructions machine natives ou de bytecode intermédiaire.
Un compilateur peut rejeter une affectation incompatible avant de produire le programme que l’utilisateur exécutera.
Compilation à la volée
La compilation à la volée génère du code machine natif pendant l’exécution du programme, souvent pour les parties fréquemment utilisées.
La compilation à la volée peut améliorer les performances d’une boucle de longue durée après que l’environnement d’exécution a observé sa répétition.
Compilation anticipée
La compilation anticipée traduit le code avant l’exécution du programme, en déplaçant le travail de traduction vers une étape de construction distincte.
La compilation anticipée peut produire un exécutable natif qui fonctionne sans que son compilateur soit installé.
Composition
La composition construit un objet à partir d’autres objets qu’il contient comme composants ou avec lesquels il coopère, exprimant une relation « possède un ».
La composition donne à une voiture un objet représentant un moteur dont elle peut appeler les méthodes sans devenir un type de moteur.
Comptage de références
Le comptage de références suit les références à un objet et récupère normalement son espace lorsque le compteur atteint zéro.
Le comptage de références de CPython a besoin de l’aide du ramassage des cycles pour les objets inaccessibles qui continuent de faire référence les uns aux autres.
Conversion implicite
Une conversion implicite est une conversion effectuée par les règles du langage lorsqu’une opération nécessite une valeur d’un autre type.
La conversion implicite de JavaScript convertit un texte numérique pour la multiplication, mais peut le concaténer lors d’une addition.

D

Débordement de tampon
Un débordement de tampon écrit au-delà des bornes d’un bloc de stockage, ce qui peut endommager les données voisines ou l’état du programme.
Écrire à l’indice 10 d’un tableau C de dix éléments crée un débordement de tampon au lieu d’ajouter un onzième élément.
Dépassement de capacité
Un dépassement de capacité se produit lorsqu’un résultat arithmétique se situe en dehors de la plage de valeurs prise en charge par une représentation numérique de taille fixe.
En Java, un dépassement de capacité entière provoque un rebouclage au-delà de la valeur maximale, tandis qu’en C, le dépassement d’un entier signé a un comportement indéfini.
Double libération
Une double libération tente de libérer deux fois le même bloc de mémoire alloué, ce qui enfreint le cycle de vie attendu par le système d’allocation.
Appeler à nouveau free sur une allocation déjà libérée peut provoquer une double libération et un comportement imprévisible.

É

Écosystème
Un écosystème est l’ensemble des bibliothèques, des outils, de la documentation et du soutien communautaire disponibles autour d’une technologie.
Un écosystème solide d’analyse de données peut rendre Python pratique pour une équipe même lorsque la vitesse brute des boucles n’est pas son principal avantage.
Éditeur de liens
Un éditeur de liens relie les fichiers objets traduits et le code des bibliothèques nécessaires, en résolvant les références entre les parties d’un programme.
Un éditeur de liens relie l’appel traduit d’un fichier source à la routine d’affichage fournie par une bibliothèque.
Évaluation en court-circuit
L’évaluation en court-circuit saute un opérande logique lorsqu’un opérande précédent détermine déjà le résultat de l’expression.
L’évaluation en court-circuit permet à une vérification de non-nullité d’empêcher une division dangereuse à droite de and.

E

Effet de bord
Un effet de bord est une action observable en plus du renvoi d’un résultat, notamment une sortie ou une modification des données partagées du programme.
Écrire un fichier de rapport est un effet de bord même si la fonction laisse toutes les variables numériques globales inchangées.
Encapsulation
L’encapsulation regroupe les données avec des opérations contrôlées qui maintiennent leurs règles et limitent les modifications dangereuses par d’autres parties du code.
L’encapsulation permet à un compte de refuser un retrait excessif avant que son solde ne devienne négatif.
Erreur d’exécution
Une erreur d’exécution se produit lorsqu’une opération exécutée ne peut pas se poursuivre selon les règles du langage ou les conditions disponibles.
Une division par zéro en Python produit une erreur d’exécution lorsque le programme atteint ce calcul.
Erreur de logique
Une erreur de logique fait calculer à un programme un résultat non voulu alors que les opérations exécutées peuvent se terminer sans échec.
Utiliser a + b / 2 pour calculer la moyenne de deux valeurs est une erreur de logique, car la division a lieu avant l’addition.
Expression
Une expression est un morceau de code évalué pour produire une valeur, qui peut ensuite être utilisée par l’instruction qui la contient.
L’expression width * height fournit la valeur renvoyée par une fonction qui calcule l’aire d’un rectangle.
Expression lambda
Une expression lambda est une expression de fonction sans nom de fonction déclaré, utile pour fournir une petite opération comme valeur.
L’expression lambda Python lambda n: n * n fournit une opération de mise au carré à une chaîne de transformation.
Expression régulière
Une expression régulière décrit un motif textuel pour un moteur de recherche de correspondances, à l’aide de règles sur les caractères, la répétition et le regroupement.
L’expression régulière [0-9]{3}-[0-9]{4} correspond à un groupe de trois chiffres suivi d’un trait d’union et de quatre chiffres.

F

Fermeture lexicale
Une fermeture lexicale est une fonction associée à l’accès aux variables de son environnement de création, qui peut survivre à l’appel englobant.
Une fermeture lexicale qui multiplie conserve son facteur après le retour de la fonction qui l’a créée.
Fonction d’ordre supérieur
Une fonction d’ordre supérieur reçoit une fonction comme argument ou renvoie une fonction, ce qui permet de combiner les comportements eux-mêmes.
La fonction d’ordre supérieur filter reçoit un prédicat qui détermine les valeurs d’entrée à conserver.
Fonction de première classe
Une fonction de première classe peut être traitée comme une valeur, notamment être affectée à un nom, transmise comme argument et renvoyée par un appel.
Une fonction de première classe peut être fournie à map comme opération à appliquer à chaque élément d’une collection.
Fonction de rappel
Une fonction de rappel est une fonction fournie à une autre partie du code pour que celle-ci puisse l’appeler lorsqu’une opération ou un événement l’exige.
Transmettre showMenu comme fonction de rappel permet à un système de bouton d’appeler cette fonction lorsque l’utilisateur clique.
Fonction pure
Une fonction pure renvoie le même résultat pour les mêmes arguments et ne produit aucun effet de bord en plus du renvoi de ce résultat.
Une fonction pure qui applique un multiplicateur fixe peut être testée sans préparer un état global ni capturer une sortie.
Fuite mémoire
Une fuite mémoire conserve un espace alloué qui n’est plus nécessaire, ce qui réduit la mémoire disponible pour un travail utile.
Un cache qui ne cesse de grandir sans éviction peut provoquer une fuite mémoire même dans une application utilisant le ramassage des miettes.

G

Gestionnaire d’événement
Un gestionnaire d’événement est une fonction ou une opération enregistrée pour répondre lorsqu’un événement particulier est transmis par l’environnement d’exécution.
Un gestionnaire de clic peut mettre à jour un menu tandis qu’un autre gestionnaire traite un message réseau entrant.
Goulot d’étranglement
Un goulot d’étranglement est la partie d’un système qui limite les performances globales, rendant moins efficaces les améliorations apportées ailleurs.
Si l’attente réseau est le goulot d’étranglement, des calculs locaux plus rapides peuvent à peine modifier le temps total de la requête.

H

Héritage
L’héritage définit une classe spécialisée à partir d’une classe existante, en réutilisant ou en étendant son comportement dans une relation « est un ».
L’héritage convient à un compte d’épargne qui doit toujours respecter le comportement attendu d’un compte bancaire.

I

Immuabilité
L’immuabilité signifie qu’une valeur existante ne peut pas être modifiée sur place ; une mise à jour doit produire ou sélectionner une autre valeur.
L’immuabilité garde une collection d’origine disponible lorsqu’une transformation construit une nouvelle version.
Implémentation
Une implémentation de langage est le logiciel qui fournit un moyen de traduire et d’exécuter les programmes écrits dans ce langage.
CPython et PyPy sont des implémentations différentes : le code Python peut donc utiliser différentes stratégies d’exécution.
Inférence de types
L’inférence de types déduit les types à partir du code environnant, ce qui permet des vérifications statiques même lorsque les annotations de type sont omises.
L’inférence de types permet à un compilateur de déterminer un type entier à partir d’une valeur d’initialisation plutôt que d’une indication de type écrite.
Instruction
Une instruction est un ordre complet d’un programme, comme une affectation, un bloc conditionnel ou une boucle.
L’instruction total = price * 3 affecte le résultat d’une multiplication à une variable.
Interface
Une interface précise les opérations que les types qui l’implémentent doivent fournir, permettant aux appelants de s’appuyer sur un contrat commun.
Une interface de forme peut exiger une méthode area() tout en laissant chaque forme la calculer différemment.
Interpréteur
Un interpréteur exécute un programme au moyen d’un système logiciel en fonctionnement, qui peut exécuter le code source ou une représentation interne.
Un interpréteur peut exécuter un court script sans fournir d’abord à l’utilisateur un exécutable natif distinct.

L

Langage d’assemblage
Le langage d’assemblage représente les instructions de bas niveau à l’aide de mnémoniques lisibles et de noms de registres ou d’emplacements de stockage.
Une personne chargée de l’analyse lit le langage d’assemblage pour suivre les opérations d’un programme dont le code source de haut niveau n’est pas disponible.
Langage de haut niveau
Un langage de haut niveau exprime les programmes à l’aide de concepts comme les variables et les fonctions, tout en masquant de nombreux détails du matériel.
Un langage de haut niveau permet de demander une somme sans choisir les registres du processeur à utiliser.
Langage dédié
Un langage dédié est conçu pour un domaine de problèmes particulier, comme les requêtes de base de données, les motifs ou la mise en forme de pages.
Un langage dédié comme SQL peut exprimer une demande de données plus directement qu’une boucle de recherche écrite à la main.
Langage multiparadigme
Un langage multiparadigme prend en charge plusieurs styles d’organisation du calcul, permettant différentes approches dans un même programme.
Python est un langage multiparadigme dans lequel une méthode de classe peut utiliser une boucle impérative et une transformation fonctionnelle.
Liaison dynamique
La liaison dynamique sélectionne une implémentation de méthode pendant l’exécution selon l’objet qui reçoit l’appel.
La liaison dynamique dirige shape.area() vers l’implémentation du rectangle lorsque l’objet qui reçoit l’appel est un rectangle.

M

Machine virtuelle
Une machine virtuelle est un logiciel qui fournit un modèle d’exécution pour des instructions, comme le bytecode utilisé par l’environnement d’exécution d’un langage.
Une machine virtuelle Java compatible permet au même fichier de classe de fonctionner sur des ordinateurs dotés de processeurs différents.
Masquage de l’information
Le masquage de l’information conserve les détails d’implémentation derrière une interface afin que les autres parties du code dépendent plutôt des opérations promises.
Le masquage de l’information permet à un compte de modifier sa représentation en mémoire tout en préservant le comportement des dépôts.
Méthode
Une méthode est une opération associée à une classe ou à un objet, qui agit souvent sur l’état de l’instance recevant l’appel.
Une méthode de retrait vérifie le montant demandé avant de modifier le solde du compte qui reçoit l’appel.
Mutation
Une mutation modifie un objet existant sur place, de sorte que toute référence à ce même objet donne accès à son contenu mis à jour.
Ajouter un élément à une liste partagée est une mutation ; associer un nom à une nouvelle liste ne modifie que cette association.

N

Nombre à virgule flottante
Un nombre à virgule flottante représente une valeur avec une précision binaire limitée, ce qui rend approximatives la plupart des fractions décimales.
Un nombre à virgule flottante peut introduire une minuscule différence entre 0.1 + 0.2 et la valeur stockée de 0.3.

O

Objet
Un objet est une entité d’un programme qui associe des données à des opérations disponibles, souvent comme instance particulière d’une classe.
Deux objets représentant des comptes peuvent utiliser la même méthode de retrait tout en conservant leurs propres soldes distincts.
Optimiseur de requêtes
Un optimiseur de requêtes choisit un plan pour exécuter une requête de base de données tout en préservant le résultat demandé par cette requête.
Un optimiseur de requêtes peut utiliser un index disponible après une modification des données, sans réécriture de la requête SQL.

P

Paradigme de programmation
Un paradigme de programmation est un style d’organisation du calcul, avec des façons particulières d’exprimer les données, les opérations et le contrôle.
Un paradigme de programmation peut mettre l’accent sur les mises à jour d’état tandis qu’un autre privilégie la composition de fonctions pures.
Paramètre
Un paramètre est un nom dans la définition d’une fonction qui reçoit une valeur fournie par un argument lorsque la fonction est appelée.
Le paramètre width reçoit 3 lorsque l’appelant évalue area(3, 4).
Pile d’appels
La pile d’appels suit les appels de fonctions actifs dans leur ordre d’imbrication afin que l’exécution puisse revenir au bon appelant.
Un appel à une fonction area ajoute une entrée à la pile d’appels et la retire lorsque son résultat revient à l’appelant.
Plateforme
Une plateforme est l’environnement matériel et logiciel sur lequel un programme doit fonctionner, notamment les environnements d’exécution et les services système pris en charge.
Une plateforme de navigateur peut exécuter JavaScript directement par son moteur et des modules compilés par WebAssembly.
Pointeur pendant
Un pointeur pendant fait référence à un espace qui a déjà été libéré et qui n’est plus une cible d’accès valide.
Un pointeur pendant peut encore contenir une ancienne adresse même après que celle-ci a été réutilisée pour d’autres données.
Polymorphisme
Le polymorphisme permet à une opération partagée de fonctionner avec différents types d’objets, chaque objet fournissant le comportement qui lui convient.
Le polymorphisme permet à un rapport d’appeler area() sur des cercles et des rectangles sans branche distincte pour chacun.
Portée
La portée est la partie d’un programme où un nom donné est disponible, indépendamment de la durée de vie de l’objet auquel il fait référence.
La portée d’un nom local ne s’étend pas à l’appelant simplement parce que la fonction renvoie l’objet que ce nom désignait.
Possession
La possession attribue la responsabilité de la durée de vie d’une valeur, ce qui permet aux règles du compilateur de vérifier les accès et d’organiser la libération de la mémoire.
La possession en Rust rejette, dans le code sûr, l’accès par une référence qui survivrait à la valeur vers laquelle elle pointe.
Programmation déclarative
La programmation déclarative spécifie un résultat ou une relation requis tout en laissant les détails de l’exécution à un moteur de langage.
La programmation déclarative permet à une requête SQL de demander les lignes qui satisfont un critère sans préciser de boucle ligne par ligne.
Programmation événementielle
La programmation événementielle organise le travail autour de gestionnaires appelés lorsque des événements se produisent, comme des clics ou des messages entrants.
La programmation événementielle attend un clic sur un bouton avant d’exécuter la mise à jour d’interface enregistrée.
Programmation fonctionnelle
La programmation fonctionnelle organise les calculs en composant des fonctions et en produisant de nouvelles valeurs tout en limitant les mutations partagées.
La programmation fonctionnelle peut exprimer un calcul de rapport sous forme de sélection, de transformation et de combinaison de valeurs.
Programmation impérative
La programmation impérative exprime un calcul sous forme de commandes ordonnées qui mettent à jour l’état du programme au moyen d’affectations et du contrôle de son déroulement.
La programmation impérative rend explicite la nouvelle valeur d’un accumulateur à chaque passage dans une boucle.
Programmation logique
La programmation logique énonce des faits et des règles, puis recherche les valeurs qui permettent à une question de satisfaire ces relations.
La programmation logique peut déduire une relation de grand-parent en reliant deux relations de parenté connues.
Programmation procédurale
La programmation procédurale regroupe les commandes impératives en fonctions ou procédures nommées, chacune chargée d’une tâche cohérente.
La programmation procédurale sépare la lecture des données, le calcul d’un résultat et l’affichage d’un rapport en tâches nommées.
Programmation structurée
La programmation structurée organise le déroulement du programme à l’aide de séquences, de structures conditionnelles, de boucles et d’appels plutôt que de sauts arbitraires.
La programmation structurée permet de suivre les blocs imbriqués pour voir quelle instruction s’exécutera ensuite.

R

Ramassage des miettes
Le ramassage des miettes identifie et récupère automatiquement l’espace des objets que le programme en cours d’exécution ne peut plus atteindre.
Le ramassage des miettes par traçage peut récupérer un groupe inaccessible même lorsque ses objets font encore référence les uns aux autres.
Récursivité
La récursivité résout un problème par des appels à la même fonction, en le réduisant généralement vers un cas qui ne nécessite aucun appel supplémentaire.
La récursivité de la factorielle multiplie par des entiers successivement plus petits jusqu’à atteindre le résultat défini pour zéro.
Redéfinition de méthode
La redéfinition de méthode donne à une sous-classe sa propre implémentation d’une méthode héritée tout en conservant l’opération disponible pour les appelants.
La redéfinition d’une méthode de retrait peut ajouter des frais tout en préservant la règle qui interdit un solde négatif.
Référence
Une référence est une valeur qui désigne un objet et permet d’y accéder sans dupliquer les données stockées par cet objet.
Copier une référence à une liste donne à un autre nom accès à la même liste et à ses mutations ultérieures.
REPL
Une REPL est une boucle interactive de lecture, d’évaluation et d’affichage qui accepte une entrée, l’évalue, montre un résultat et attend la suivante.
Une personne utilise une REPL pour tester une petite expression et voir son résultat avant d’en saisir une autre.
Repliement des constantes
Le repliement des constantes évalue pendant la compilation les expressions constituées de constantes tout en préservant le comportement requis du programme.
Le repliement des constantes permet à un compilateur de remplacer 60 * 60 * 24 par 86 400 avant le démarrage du programme.

S

Sémantique
La sémantique est le sens attribué aux constructions d’un programme, notamment le comportement des opérateurs et la façon dont l’exécution modifie les valeurs.
La sémantique de la division entière explique pourquoi une division d’apparence identique peut donner des résultats différents selon le langage.
SQL
SQL est un langage de manipulation de données relationnelles dont les requêtes indiquent les champs demandés, les conditions sur les lignes et l’ordre souhaité.
SQL peut demander les noms des élèves dont la note dépasse un seuil et trier ces noms par ordre alphabétique.
Sûreté mémoire
La sûreté mémoire empêche certaines catégories d’accès mémoire invalides, comme l’accès à un espace libéré ou l’écriture au-delà des bornes valides.
La sûreté mémoire peut bloquer un accès dangereux alors que le programme contient toujours une erreur sans lien dans son calcul.
Syntaxe
La syntaxe est l’ensemble des règles qui définissent les arrangements d’unités lexicales formant des expressions, des instructions et des programmes valides.
La syntaxe Python exige des deux-points après une condition if et une indentation pour le bloc contrôlé.

T

Tableau de suivi d’exécution
Un tableau de suivi d’exécution enregistre les valeurs pertinentes des variables après les étapes exécutées afin de suivre le chemin et les changements d’état.
Un tableau de suivi d’exécution pour un accumulateur indique le nouveau total après chaque passage d’un élément dans la boucle.
Tas
Le tas est une zone mémoire utilisée pour les objets alloués dynamiquement, notamment ceux qui peuvent survivre à la fonction qui les crée.
Une liste renvoyée peut rester sur le tas après la fin de la fonction qui l’a créée et la disparition de son bloc d’activation.
Turing-complétude
La Turing-complétude est la capacité d’exprimer tout algorithme calculable lorsqu’un temps et une mémoire idéalisés sans limites sont disponibles.
La Turing-complétude n’indique pas à une équipe si un langage dispose de la prise en charge par navigateur ou des bibliothèques nécessaires à son projet.
Typage canard
Le typage canard accepte un objet selon les opérations qu’il fournit, sans nécessairement exiger une superclasse commune déclarée.
Le typage canard de Python permet à un objet sans lien avec les autres de participer à un calcul d’aire s’il fournit une méthode area() compatible.
Typage dynamique
Le typage dynamique vérifie les types des valeurs pendant l’exécution et permet à un nom de faire référence à des valeurs de types différents au fil du temps.
Le typage dynamique de Python permet à un nom de passer d’un entier à une chaîne de caractères, mais une opération arithmétique incompatible échoue toujours.
Typage statique
Le typage statique vérifie la compatibilité des types avant l’exécution, ce qui permet de rejeter de nombreuses combinaisons invalides pendant le développement.
Le typage statique permet à un compilateur Java de rejeter du texte utilisé pour initialiser une variable entière avant l’exécution du programme.
Type de données
Un type de données définit un ensemble de valeurs possibles et les opérations autorisées sur ces valeurs, ce qui guide leur représentation et leur utilisation.
Un type de données chaîne de caractères permet des opérations sur du texte même lorsque les caractères de la valeur sont des chiffres.

U

Unité lexicale
Une unité lexicale est un élément porteur de sens du texte source, comme un mot-clé, un identifiant, un littéral, un opérateur ou un signe de ponctuation.
Le point-virgule d’une affectation en C est une unité lexicale même s’il ne nomme ni une variable ni une valeur numérique.
Utilisation après libération
L’utilisation après libération consiste à accéder à un espace par un pointeur après la libération de son allocation, ce qui provoque un accès mémoire invalide.
Une utilisation après libération peut écraser un nouvel objet qui occupe un bloc précédemment libéré.

À propos de cette unité

Par StudeliSignaler une erreur

Crée ton propre Matériel d'Étude

Transforme tes notes, diapos et documents en Guides d'Étude, Questions Pratiques, Flashcards et 6 autres outils.

Commencer