Introduction : les paradigmes de programmation

Paradigmes de programmation

Un paradigme de programmation est une manière de programmer un ordinateur basé sur un ensemble de principes ou une théorie.

Un paradigme de programmation peut être vu comme la philosophie d’un langage. Il prédétermine une manière d’écrire propre à celui-ci, définit une idiomatique conventionnelle (c’est-à-dire, par exemple, une manière d’éviter de copier-coller 500 fois le même paragraphe). Ces paradigmes sont des archétypes ; en réalité, les langages modernes permettent souvent plusieurs paradigmes. Ceux-ci ne correspondent donc pas à une sorte de réglement implacable mais énoncent plutôt différentes manières de structurer du code. Ce qui importe, au fond, c’est d’être suffisamment avisé pour pouvoir faire des choix.

Trois principaux paradigmes dominent le monde actuel de la programmation. La programmation procédurale, la programmation objet, et la programmation fonctionnelle (cette dernière est le paradigme principal de R).

La programmation procédurale

La programmation procédurale est le plus ancien paradigme de programmation. Il s’agit d’une séquence de procédures, c’est-à-dire une série d’étapes à réaliser pour faire quelque chose. On peut voir cela comme une recette de cuisine.

Supposons que l’on veuille peindre un mur en vert, et que la procédure mélanger_couleurs_sur_palette(palette) mélange les deux premières couleurs de la palette pour stocker le résulat de ce mélange au troisième emplacement de la palette, on pourrait faire :

mettre_sur_palette(palette, bleu)
mettre_sur_palette(palette, jaune)
mélanger_couleurs_sur_palette(palette)
peindre_mur(mur, palette[3])

Il faut bien mesurer ce qui se passe à la fin de la dernière ligne : on a sélectionné la couleur de la palette en appelant son emplacement numéro 3 avec palette[3]. Cela suppose d’une part que l’on connait le fonctionnement interne des palettes, et d’autre part que cette structure interne ne change jamais. Si l’on change le code interne de la palette, il faudra aussi changer notre code. Notre programme dépend de l’implémentation de la palette.

Toutefois, la programmation purement procédurale n’est pas sans intérêt dans les langages proches du matériel (on les appelle “de bas niveau”). Elle est par exemple utilisée dans le langage C. Le véritable langage des machines est en effet en lui-même procédural ; un processeur est essentiellement un composant électronique qui manipule des états de manière séquentielle. La programmation procédurale pour autant pas restreinte aux langages de bas-niveau ; Python notamment est un peu fourre-tout, mais reste relativement procédural.

La programmation orientée objet (POO)

La programmation orientée objet a émergé du besoin de faire apparaître une syntaxe disciplinée et plus proche d’une perception humaine. Pour ce faire, on s’inspire du monde réel : il n’y a pas besoin de connaitre le fonctionnement électronique d’une télécommande pour appuyer sur ses boutons. Il n’est pas attendu non plus que l’utilisateur d’une télévision aille bidouiller ses circuits imprimés pour s’en servir. Dans un langage objet, l’exemple précédent pourrait s’écrire :

palette.ajouter(bleu)
palette.ajouter(jaune)
palette.mélanger()
mur.peindre(palette.get_couleur())

On voit que palette, une instance d’objets de classe palette, possède a minima trois méthodes (ajouter, mélanger, et get_couleur). Puisqu’on s’est astreints à ne jamais accéder aux états internes de la palette, on a découplé le programme de peinture de celui des palettes. On n’a plus besoin de savoir comment est structurée intérieurement une palette (on ne sélectionne plus son troisième élément). La différence est de taille : la personne qui s’occupe de la maintenance des palettes peut maintenant les changer comme elle veut sans risquer de causer une erreur dans le programme de peinture (par exemple un mur jaune fluo à poids mauves). La seule chose qui doit être respectée, c’est le comportement des différentes méthodes.

Note

Java et C++ sont des langages objet populaires.

La programmation fonctionnelle

Là où la POO s’inspire des objets dans le monde réel, la programmation fonctionnelle s’inspire du monde des mathématiques. L’idée fondamentale de la programmation fonctionnelle est de ne plus manipuler d’états ; on n’utilise que ce qu’on appelle des “fonctions pures”. Celles-ci se comportent comme une calculatrice sans mémoire. Elles font un calcul purement combinatoire ; elles sont prévues pour retourner toujours le même résultat à partir des mêmes entrées.

mur = mur_peint(couleur = mélanger(jaune, bleu))

La syntaxe fonctionnelle étant proche d’une écriture mathématique, elle convient aux langages de haut niveau prévus pour le calcul scientifique, comme R. Pendant longtemps (jusqu’aux années 2010), ce paradigme a été considéré comme assez académique et réservé aux profs de facs. Depuis les années 2010, cependant, le paradigme fonctionnel a rencontré un très net regain de popularité et est sorti des universités. Ses principaux intérêts sont :

  • la grande facilité de déboggage et de test (une entrée doit toujours retourner la même sortie)
  • la beauté/maintenabilité du code
  • la forte parallélisabilité des calculs : les résultats ne dépendant que de leurs entrées, il est potentiellement possible de calculer plusieurs choses en même temps si l’on dispose de plusieurs CPU. C’est notamment cet aspect qui permet par exemple au package arrow d’être extrêmement rapide ; les calculs sont faits de manière possiblement concourrante plutôt que consécutive.
Note

Outre R, Haskell, Scala et Lisp sont des langages fonctionnels.

R : un langage fonctionnel, vectoriel, avec quelques éléments de POO

R est très majoritairement un langage fonctionnel. Dans la mesure où aucun langage n’est entièrement soluble dans un paradigme, il présente également quelques petits traits de programmation objet tordus pour correspondre au coeur d’un langage fonctionnel. Le principal système de programmation objet en R, S3 (qui n’a rien à voir avec le protocole de stockage même s’il s’appelle similairement), adapte les principes de la POO dans le contexte de la programmation fonctionnelle.

Un autre aspect particulier de R est qu’il est vectoriel. Si je fais :

La vectorisation est tout-à-fait implicite. Dans un langage procédural non-vectorisé, j’aurais plutôt écrit :

Cette dernière écriture est équivalente mais n’est pas très pratique pour écrire des statistiques….

Note

L’opérateur pipe %>% repose sur les axiomes de la programmation fonctionnelle. Il suppose en effet que chaque sortie de fonction ne dépend que des entrées. Il permet de profiter à la fois de la simplicité d’une écriture de type “recette de cuisine” et des avantages de la programmation fonctionnelle.

Quizz

Question 1

Dans un langage fonctionnel idéal :

Question 2

Quel est le rapport entretenu entre R et la Programmation Orientée Objet ?

R est un archétype de langage de POO.

On ne peut pas du tout faire de POO en R.

Objection votre honneur !

R est surtout un langage fonctionnel, mais il présente quelques aspects de POO et de programmation procédurale.

Question 3

Un langage de bas niveau est un langage caractérisé par sa simplicité.

Non, c’est un langage proche du langage machine, donc souvent assez complexe et verbieux.

Exactement ! C’est un langage prévu pour être facile d’appréhension, d’où de “bas niveau”.

Non, un langage de bas-niveau est un langage est un langage qui est calculé dans la partie basse du chipset de la carte mère (le southbridge), tandis que les langages de haut niveau sont déterminés par sa partie haute (le northbridge).

Non, un langage de bas-niveau est un langage stromosogomique de capacitance inférimitésimale.

Question 4

Analysons ce pseudo-code :

classeur_ods = nouveau_classeur_ods()
onglet = nouvel_onglet(donnee = “En voilà de la donnée”)
classeur_ods.ajouter_onglet(onglet)
classeur_ods.enregistrer(“/home/tintin/mon_classeur.ods”)

À quel paradigme de programmation cela ressemble ?

Langage procédural

Langage objet

Langage fonctionnel

Question 5

Analysons ce pseudo-code :

a = [‘b’, ‘o’, ‘n’, ‘j’, ‘o’, ‘u’, ‘r’, ’ ’]
i = 1
while a[i] != ’ ’ {
  print(a[i])
  print(a[i])
}

À quel paradigme de programmation cela ressemble ?

Langage procédural

Langage objet

Langage fonctionnel

Question 6

Analysons ce pseudo-code :

racine_carrée(multiplier_par([entiers de 1 à 10], 3))

À quel paradigme de programmation cela ressemble ?

Langage procédural

Langage objet

Un langage fonctionnel ! Et vectoriel en plus ! C’est presque R !

Question 7

On a vu qu’un langage objet permet notamment d’éviter les dépendances à l’implémentation.

On préfère écrire :

mur.peindre(palette.get_couleur())

Que :

peindre_mur(mur, palette[3])

Quel intérêt cela présente ?

Aucun, c’est purement esthétique.

Cela permet à un collègue d’être libre de modifier le fonctionnement interne de la palette sans faire bugger le programme de peinture.

Compter jusqu’à 3 est difficile car un ordinateur repose sur des nombres binaires. On préfère donc enlever le 3.

On a vraiment envie que le programme de peinture dépende du programme de la palette. Il faut que les gens travaillent en équipe et donc qu’ils soient chacun au courant de tout ce qu’il se passe dans l’intégralité du projet.

Question 8

Combien de méthodes différentes de la classe discman ce pseudo-code utilise-t-il ?

discman.brancher_ecouteurs(ecouteurs)
discman.inserer_cd(never_mind_the_bollocks_here_s_the_sex_pistols)
discman.ecouter_cd()
discman.retirer_cd()
discman.inserer_cd(peace_sells_but_who_s_buying)
discman.ecouter_cd()
discman.retirer_cd()

1

2

3

4

5

6

7