Langages de programmation
Un langage de programmation a besoin :
- des règles de grammaire qui définissent la syntaxe des expressions ;
- d’une sémantique qui définit le sens des expressions.
Un langage peut être :
- interprété : un interpréteur lit et analyse le code séquentiellement, le traduit en langage machine et lance son exécution.
- compilé : un compilateur lit et analyse le code puis le traduit en langage machine. Par la suite l’exécutable généré peut être lancé.
Remarque : Python un langage interprété mais le code n’est pas directement traduit dans le langage machine de l’ordinateur sur lequel le programme est lancé mais dans le langage machine d’une machine virtuelle (bytecode). Dans un second temps, ce langage machine est interprété par le logiciel.
Paradigme de programmation
On appelle paradigme de programmation une façon de se représenter un problème informatique et sa résolution.
Le paradigme choisi a une très grande influence sur la manière de concevoir un programme, il constitue donc un des critères principaux pour le choix d’un langage.
Il existe un grand nombre de paradigmes de programmation, le programme de la NSI se concentre sur la programmation impérative, la programmation fonctionnelle et la programmation objet.
Certains langages sont très liés à certains paradigmes mais la plupart permettent de mettre en œuvre plusieurs paradigmes ; c’est le cas de Python :
- Impératif : Simple statements et Compound statements ;
- Fonctionnel : Functional Programming HOWTO ;
- Objet : Classes.
Programmation impérative
WikipediaLes « langages impératifs » sont les plus répandus parmi l’ensemble des langages de programmation et sont ceux qui sont les plus proches du fonctionnement des processeurs : exécuter une suite d’instructions élémentaires.
Les instructions de base de ce paradigme sont :
- la séquence d’instructions : les instructions sont exécutées les unes après les autres ;
- l’assignation ou affectation : on stocke ou on modifie une information en mémoire (dans une variable) ;
- l’instruction conditionnelle : un bloc d’instruction n’est effectué que si une certaine condition est réalisée ;
- la boucle : une séquence d’instructions est répétée un certain nombre de fois (boucle
Pour
) ou jusqu’à ce qu’une condition soit réalisée. - les branchements : la séquence d’instruction est transférée à un autre endroit dans le code.
De nombreux langages incluent un
goto
permettant de réaliser un branchement ou la possibilité d’écrire des fonctions — qui ne sont dans ce cas qu’une suite d’instructions (et donc n’ont aucun sens mathématique).
Mots clés pour repérer un langage impératif
- affectation, mémoire, instruction, boucle, mutabilité, effets de bord, …
Exemples de langages impératifs
- Langage machine, C, Bash, Perl, Tcl, Python, PHP, Java, JavaScript, …
Usage typique
- Lorsque le programme à réaliser s’exprime sous forme d’actions à réaliser. Les actions reflètent les changements dans l’environnement (mémoire) du programme.
- Dans le cadre de la programmation système, pour une gestion manuelle, potentiellement fine, de l’allocation mémoire.
Programmation fonctionnelle
Wikipedia- La programmation fonctionnelle s’affranchit de façon radicale des effets de bord en interdisant toute opération d’affectation.
- La manipulation de variables est remplacée par la composition de fonctions (boîtes noires imbriquées les unes dans les autres).
- Les langages purement fonctionnels remplacent les boucles par la récursivité.
- Les langages fonctionnels mettent tous en œuvre une gestion automatique de l’allocation mémoire.
- Dans les langages fonctionnels les fonctions sont des citoyennes de première classe : une fonction peut recevoir une fonction comme argument, une fonction peut retourner une fonction.
Remarque
-
La mise en œuvre des langages fonctionnels fait un usage sophistiqué de la pile (cf. chapitre 1 sur la récursivité et chapitre sur la structure de pile) car, afin de s’affranchir de la nécessité de stocker des données temporaires dans des tableaux, ils font largement appel à la récursivité. L’une des multiples techniques pour rendre la compilation de la récursivité plus efficace est une technique dénommée récursion terminale (en anglais : tail-recursion), qui consiste à accumuler les résultats intermédiaires dans une case mémoire de la pile et à la passer en paramètre dans l’appel récursif. Ceci permet d’éviter d’empiler les appels récursifs dans la pile en les remplaçant par une simple succession de sauts. Le code généré par le compilateur est alors similaire à celui généré par une boucle en impératif.
-
La programmation fonctionnelle éliminant les effets de bord (fonctions pures), il est plus facile de faire effectuer des calculs en parallèle aux programmes développés dans un style fonctionnel pur.
Mots clés pour repérer un langage impératif
- déclaratif, expressions, immutabilité, ordre supérieur, $\lambda$-calcul, fonction pure, …
Exemples de langages fonctionnels
- Haskell, Lisp, Scheme, OCaml, …
Usage typique
- Manipulations de haut niveau ;
- Calculs algébriques, abstraits ;
- Écriture de compilateurs.
Comparaison des deux styles de programmation
Exercice 1 : illustration des différences entre les style impératif et fonctionnel
Définir une fonction qui réalise la somme des nombres d’une liste passée en argument,
- En style impératif.
- En style fonctionnel.
-
Style impératif
Cette fonction manipule la variable locale1 2 3 4 5 6 7 8
def somme(liste: list[float]) -> float: """ Calcule la somme des éléments de la liste liste. """ somme = 0 for nbre in liste: somme = somme + nbre return somme
somme
et utilise une bouclefor
. Le paradigme utilisé est bien impératif. -
Style fonctionnel
Cette fonction ne manipule aucune variable et n’utilise pas de boucle mais la récursivité. Le paradigme employé est bien fonctionnel.1 2 3 4 5 6 7 8
def somme(liste: list[float]) -> float: """ Calcule la somme des éléments de la liste liste. """ if len(liste) == 0: return 0 else: return liste[0] + somme(liste[1:])
Remarque : liste[1:]
est une sous-liste de liste
dans laquelle le premier élément de liste
n’est pas présent.
- Style fonctionnel avec récursion terminale
1 2 3 4 5 6 7 8
def somme(liste: list[float], total: float = 0) -> float: """ Calcule la somme des éléments de la liste liste. """ if len(liste) == 0: return total else: return somme(liste[1:], liste[0] + total)
Exercice 2 : un exemple de fonction non pure
Écrire une fonction (sans grand intérêt !!!) dont la spécification est la suivante :
|
|
|
|
Il est évident que deux appels successifs à la fonction elevation_puissance
avec le même argument (2 par exemple) pourront renvoyer des valeurs différentes puisque celles-ci dépendent de la valeur entrée par l’utilisateur. Cette fonction n’est pas une fonction pure ; sa définition n’est pas acceptable du point de vue de la programmation fonctionnelle.
Exercice 3 : illustration d’un effet de bord
Définir une fonction qui élève au carré chaque élément d’une liste.
- En style impératif (avec effet de bord possible).
- En style fonctionnel (sans effet de bord).
- Style impératif avec effet de bord.
La fonction modifie la liste globale
1 2 3 4 5 6 7 8 9
def liste_carre(liste: list[float]) -> None: """ Élève au carré chaque élément de la liste liste. """ for i in range(len(liste)): liste[i] = liste[i]**2 l = [1, 2, 3, 4] liste_carre(l)
liste
! Le problème a pour origine la mutabilité de la structure de données « liste » et le passage d’une référence à la fonction, et non pas une copie de cette liste.
|
|
+
appliqué à des opérandes du type « séquence » génère une nouvelle liste en Python.
Programmation objet
Mettre en œuvre ce paradigme nécessite donc de modéliser le problème par une interaction d’objets dont on définit les caractéristiques.
- Un objet possède un état (des champs ou attributs) et un comportement (des méthodes).
- Une classe est un patron permettant de fabriquer des objets. Une classe est aussi la définition d’un nouveau type de données.
Mots clés pour repérer un langage objet
- classes ou prototype, objet, méthode, attribut, champ, statique, encapsulation, héritage, polymorphisme, …
Exemples de langage orientés objets
- C++, Java, Python, OCaml, …
Usage typique
- Développement en équipe de logiciels comportant un très grand nombre de lignes de code.