Introduction à la programmation

De EduTech Wiki
Aller à la navigation Aller à la recherche
Aspects théoriques de la pensée computationnelle
◀▬▬▶
débutant
2024/09/26
Catégorie: Aspects théoriques de la pensée computationnelle

Introduction

Cette introduction à la programmation a pour but de fournir à des personnes sans un background technique les éléments conceptuels principaux pour s’initier à la programmation, indépendamment du langage ou des technologies envisagés, dans une perspective End-User Programming ou End-User Development (Lieberman et al., 2006). Pour ce faire, nous allons d’abord situer la programmation dans le contexte plus large du développement. Ensuite, nous proposerons trois types d’approches différentes qui sont toutefois complémentaires à la programmation.

  1. La première approche, plutôt conceptuelle, introduit la programmation dans une perspective fidèle au contexte de ce wiki, c’est-à-dire en utilisant des concepts issus des Sciences de l’Éducation et de la Psychologie.
  2. La deuxième approche, plus technique, illustre la fonction de médiation que la programmation joue entre le développeur et la machine.
  3. La troisième approche, plus pragmatique, essaye de mettre ensemble les approches conceptuelles et techniques pour illustrer les aspects fondamentaux de la programmation.

En guise de conclusion, nous proposons une liste de ressources liées à la programmation.

La programmation dans le cycle de développement

Les trois grandes étapes du cycle de développement.

Le développement est un processus qui est souvent considéré comme un cycle. Il existe plusieurs méthodologies de développement qui déterminent de manières différentes le nombre et l’ordre (e.g. séquentiel ou itérative) des étapes, mais en général on peut regrouper les étapes en trois grandes parties :

  • Les besoins (requirements en anglais), ou plus généralement tout ce qui précède, motive et/ou justifie le développement ;
  • La construction (McConnell, 2004), c'est à dire toutes les activités nécessaires à l'implémentation concrète de l'application ;
  • Le test ou, plus en général, tout ce qui se passe une fois que l'application/logiciel est disponible.

Les trois phases ont bien entendu des recouvrements et peuvent s'influencer mutuellement à plusieurs reprises pendant le cycle. Néanmoins, la phase de construction, même si elle n'est peut-être pas la plus importante d'un point de vue "philosophique", demeure la seule qui est strictement fondamentale pour pouvoir obtenir, enfin, une application fonctionnelle.

De manière plus détaillée, on peut identifier les étapes fondamentales suivantes :

  1. Analyse des besoins et/ou du contexte d’utilisation
    Développer signifie créer quelque chose qui répond à une certaine nécessité, représentant ainsi une solution à une exigence d'utilisateur(s).
  2. Prototypage
    L’application à développer est déterminée de manière conceptuelle en fonction des objectifs et de la manière de les achever. Selon le type de méthode adoptée, à ce stade, on fait souvent des dessins de l’interface, de scénarios d’utilisation, etc.
  3. Implémentation
    L’application est « traduite » dans une forme interprétable par le dispositif sur lequel elle est censée fonctionner. Cette étape correspond normalement à la programmation, c’est-à-dire à l’écriture du code et à la génération de tous les éléments nécessaires au fonctionnement (e.g. éléments graphiques, ...). McConnell (2004, p.16) suggère d'utiliser la métaphore de la construction :
    « The image of "building" software is more useful than that of "writing" or "growing" software. It's compatible with the idea of software accretion and provides more detailed guidance. Building software implies various stages of planning, preparation, and execution that vary in kind and degree depending on what's being built. »
  4. Test
    L’application est contrôlée dans le but qu’elle fonctionne de la manière imaginée/souhaitée.
  5. Déploiement
    L’application est rendue disponible aux utilisateurs (e.g. publiée sur le web).
  6. Maintien
    L’application est modifiée ou adaptée à des nécessités qui se présentent dans le temps.

Cette page s’intéresse principalement à la phase d’implémentation, mais considère la programmation comme un élément intégré dans le cycle de développement.

Approche conceptuelle à la programmation

L’approche behavioriste des utilisateurs

"Approche utilisateur" limitée aux stimuli et réponses de l'application, pas au fonctionnement interne.

La plupart des personnes qui utilisent un logiciel ou une application adopte une approche qu’on pourrait qualifier de behavioriste. En effet, les utilisateurs ne se préoccupent pas de comprendre ce qui se passe à l’intérieur de la « boîte noire » de l’application, mais ils se limitent à évaluer ce que l’application permet de faire en termes de manipulations (i.e. les stimuli) et les résultats qu’on peut obtenir (i.e. les réponses).

Dans le cadre de la programmation, le schéma « stimulus-réponse » est assez proche du principe du « input-output » (abrégé I/O) : à un certain input introduit dans le système correspond un output.

Le principe du Input/Output

Mécanisme du Input/Output (I/O)

Le rôle du programmeur est donc de déterminer ce qui se passe à l’intérieur de la « boîte noire », c’est-à-dire de déterminer quel comportement l’application va adopter sur la base des stimuli fournis par les utilisateurs. Plus spécifiquement, dans la plupart des cas, programmer est le fait de fournir aux utilisateurs la possibilité de transformer des inputs dans des outputs, car jugées plus utiles, intéressantes et/ou adaptées à leurs besoins.

Le mécanisme d’I/O peut se faire à différents niveaux, pouvant discriminer l’input de l’output ce qui signifie que cela dépend parfois de la perspective adoptée. Voici quelques exemples très simples pour illustrer le principe :

  • Dans une application qui permet de faire des calculs, l’utilisateur connait deux chiffres ou plus qu’il désire associer mathématiquement (inputs) pour obtenir un résultat qu’il ne connaît pas (output).
  • Dans une application de traitement de texte, l’utilisateur saisit des mots qui forment des phrases (inputs) et applique des transformations sur le texte (inputs) pour obtenir un document final (output) ; en même temps, il peut également partir d’un document existant (output) pour sélectionner une partie spécifique qui l’intéresse (input) et la sauvegarder dans un nouveau document (output).
  • Dans un jeu vidéo « point and shoot », l’utilisateur fournit plusieurs instructions d'input (bouger, tirer, etc.) et l’application fournit des outputs correspondant (augmenter les points, passer de niveau, etc.). En même temps, le jeu crée automatiquement des obstacles ou des ennemis (output) qui déterminent les actions du joueur (input).
  • Dans un QCM online, le système propose des questions à choix multiples (output) parmi lesquels l’utilisateur doit identifier et sélectionner la réponse correcte (input). Un feedback (output) est finalement fait à l’utilisateur qui lui dit s'il a choisi la bonne réponse.

L’interaction personne-machine

Le cycle I/O dans l'interaction personne-machine.

Le principe de l’I/O est à la base de l'interactive computing et de l’interaction personne-machine (IHM ou HCI an anglais, pour Human-Computer Interaction). En effet, en simplifiant les choses, on peut illustrer l’interaction entre l'utilisateur et la machine à travers une série d’échanges cycliques qui prévoie de l’I/O du côté de la machine, et du stimulus/réponse (ou comportement) chez l’utilisateur :

  1. L’utilisateur obtient des stimuli depuis l’application, par exemple à travers des instructions à faire, des éléments à manipuler (boutons, champ de texte, etc.), des feedbacks, etc.
  2. L’utilisateur transforme ces stimuli en comportement (e.g. en cliquant sur un bouton).
  3. Ce comportement représente un input pour l’application, qui détermine un output correspondant (e.g. afficher une nouvelle page).
  4. Cet output représente un nouveau stimulus pour l’utilisateur. On revient donc au point 1, à moins que l’output ne soit considéré définitif (par l’utilisateur et/ou l’application).

Dans cette perspective, les trois aspects qui déterminent l’ergonomie d’une application selon le standard ISO 9241-11 - « the extent to which a product can be used by specified users to achieve specified goals with effectiveness, efficiency and satisfaction in a specified context of use » - peuvent être analysés par rapport à ces échanges entre la personne et la machine :

  • Efficacité
    Une application est efficace si elle permet d’obtenir l’output souhaité à partir d’inputs identifiables.
  • Efficience
    Une application est efficiente si le nombre d’inputs nécessaires est proportionnel à la qualité/complexité de l’output.
  • Satisfaction
    Une application est satisfaisante si les échanges I/O vs. stimulus/réponse entre la personne et la machine sont considérés de manière positive par l’utilisateur.

Pensée computationnelle

Pour effectuer la transformation I/O, il faut que le développeur explicite des algorithmes qui permettent de transformer un input donné dans l'output correspondant. La conception de ces règles peut être plus ou moins complexe selon la taille et les objectifs de l'application. La pensée computationnelle (ou CT, depuis computational thinking en anglais) est un terme se référant, de manière plutôt flexible, à un ensemble d'habilités, compétences et procédures qui facilitent la résolution de problèmes en s'appuyant sur des principes tirés des sciences informatiques (Computer Science). Aho (2012, p. 832) définit la pensée computationnelle de la manière suivante : « [w]e consider computational thinking to be the thought processes involved in formulating problems so their solutions can be represented as computational steps and algorithms».

La liste des concepts associés à la pensée computationnelle peut varier en taille et spécificité, mais de manière générale on retrouve souvent les 4 éléments suivants :

  1. Décomposition
    L’action de diviser un problème complexe ou un système en petites parties plus simples à gérer ;
  2. Reconnaissance de pattern
    L’action d’identifier des similarités entre problèmes ou à l’intérieur du même problème donné ;
  3. Abstraction
    L’action de se focaliser exclusivement sur les parties importantes du problème ;
  4. Algorithmes
    L’action de trouver une solution à travers une règle composée par une série d’étapes.

Depuis quelques années, le concept de pensée computationnelle est à la base d'un débat sur le rôle et l'utilité de l'enseignement de l'informatique à l'école. On peut identifier en gros deux lignes de pensée :

  • La première soutient que la pensée computationnelle est une compétence fondamentale qui doit être enseignée au même titre que la lecture, écriture et arithmétique, car elle peut être généralisée à tout autre domaine (Wing, 2006)
  • La deuxième affirme qu'il n'y a aucune évidence qui confirme le transfert de connaissances à d'autres domaines (il y a plutôt l'évidence du contraire), mais c'est plutôt les autres domaines qui incluent de plus en plus des aspects computationnels, d'où l'utilité de la pensée computationnelle (Guzdial, 2015; Denning, 2016; Tedre & Denning, 2016).

Sur la base de cette deuxième conception de la pensée computationnelle, Denning (2016, p. 33) propose de recadrer la définition d'algorithme : « an algorithm is not any sequence of steps, but a series of steps that control some abstract machine or computational model without requiring human judgment. Computational thinking includes designing the model, not just the steps to control it. »

On retrouve dans cette définition le concept de modèle qui est également utilisé en psychologie et sciences de l'éducation (e.g. Langage de modélisation pédagogique). Tout comme dans ces domaines, où les modèles sont censés décrire le fonctionnement d'un principe ou d'un concept en tenant compte des caractéristiques du "système" qu'ils décrivent (e.g. modèle de la mémoire de travail), les algorithmes en programmation doivent tenir compte des particularités du dispositif numérique. Pour cette raison, dans la section suivante, nous allons illustrer les aspects techniques qui permettent (ou au contraire entravent) l'application d'un modèle computationnel abstrait à une machine physique.

Approche technique à la programmation

Le langage de programmation

De manière concrète, la programmation permet de résoudre un problème (ou un besoin) de manière automatisée grâce à l’application d’un algorithme. Un programme est l’application d’un algorithme dans un langage de programmation particulier.

Un algorithme quant à lui est une suite d’expressions (ou instructions) qui sont évaluées par le processeur du dispositif sur lequel « tourne » le programme. La totalité des expressions utilisées dans un programme représente le « code source ». Pour que le dispositif puisse exécuter le code source, il faut utiliser un langage que la machine peut comprendre : un langage de programmation.

Langage de bas vs. haut niveau

Il existe plusieurs langages de programmation et différentes manières pour les différencier. Une des distinctions les plus communes divise les langages en bas et en haut niveau. Cette distinction peut s’expliquer en partant du présupposé que machines et humains parlent deux langues différentes et nécessitent donc une sorte de compromis pour se comprendre :

  • Un langage de bas niveau peut se considérer plus proche du langage de la machine (i.e. binaire) plutôt que du langage humain. Un langage de bas niveau est plus difficile à apprendre et à utiliser, mais il permet néanmoins plus de possibilités d’interaction avec le hardware de la machine.
  • Un langage de haut niveau, au contraire, est plus proche du langage des êtres humains, et il est par conséquent plus facile à utiliser. Cependant, cette facilité limite les possibilités d’interagir avec la machine selon ce que le langage met à disposition.

Cette distinction peut aussi se voir de manière non binaire : un langage peut-être plus ou moins de haut niveau.

Compilation vs. Interprétation

Un langage de programmation nécessite donc une « traduction » pour que la machine puisse le comprendre et l’exécuter. La plupart des dispositifs numériques fonctionnent en effet avec le système binaire : toute l'information (fichiers, logiciels, actions, etc.) contenue ou mise à disposition par un ordinateur, par exemple, existe sous forme de suites numériques composées exclusivement de 0 et de 1, regroupées en octets (suite de 8 valeurs binaires ou bits). Il est donc nécessaire de transformer les instructions dans un langage machine. Traditionnellement, ce mécanisme peut se faire de deux manières différentes :

  • Compilation : la compilation d’un programme consiste à transformer toutes les instructions (i.e. le code source) en langage machine avant que le programme puisse être exécuté et, par conséquent, il sera nécessaire de refaire la compilation après des changements apportés au code source. Par exemple, les logiciels installés sur les ordinateurs (.exe pour Windows, .app pour Mac) ont été compilés, c’est pourquoi une nouvelle installation est nécessaire pour les mises à jour des logiciels.
  • Interprétation : dans le cas des langages de programmation qui ne sont pas compilés, il est nécessaire qu'un interprète (aussi appelé interpréteur) lise et traduise les instructions lors de l'exécution (ou en anglais, « on run time »). Dans ce cas, le code source est lu à chaque exécution ; par conséquent, les changements apportés au code seront pris en compte directement. Cependant, pour ce faire, il est nécessaire que la machine sur laquelle le programme tourne dispose de l’interprète. Par exemple, les langages de programmation pour le web sont des langages qui disposent d’un interprète soit au niveau du serveur (PHP, Ruby, etc.), soit dans le navigateur de l’utilisateur (JavaScript).

Des langages pour les êtres humains

Le code source est "traduit" en code binaire.

Du moment qu'on n'écrit plus en code binaire, les langages de programmation s'approchent de tout autre langage humain. En effet, du moment que les interprètes/compilateurs traduisent le langage de haut niveau en langage machine, les caractéristiques de celui-ci peuvent être déterminées de manière arbitraire. Imaginons une instruction qui permet d'afficher un simple message à l'écran: on peut l'écrire avec deux langages de programmation différents, mais la "traduction" en code binaire sera équivalente (ou assez proche), car la machine va exécuter la même opération dans les deux cas.

Un langage de programmation partage ainsi plusieurs caractéristiques avec une langue naturelle :

  • Lexique
    Il dispose d’un lexique, c’est-à-dire une liste de mots qui ont une signification particulière ;
  • Syntaxe
    Il dispose d’une syntaxe, c’est-à-dire des règles d’associations de mots et de la ponctuation ;
  • Combinaison
    Il est de nature combinatoire, car on peut produire différentes expressions et potentiellement un code source infini ;
  • Conventions
    Il est de nature conventionnelle et arbitraire, car le fait d’utiliser certains mots et une certaine syntaxe est décidé par les créateurs du langage ;
  • Évolution
    Il évolue dans le temps, car de nouveaux mots ou de nouvelles possibilités syntaxiques peuvent s’ajouter ou disparaître du langage lorsqu’une nouvelle version est introduite.

F(X) = Y

La computation consiste à utiliser une fonction (dans le sens générique) pour transformer des inputs dans des outputs.

Bien que les langages de programmation, surtout de haut niveau, partagent plusieurs caractéristiques avec le langage humain, l'objectif principal d'un langage de programmation reste celui d'instruire la machine pour qu'elle produise des outputs conformément aux objectifs de l'application.

Si on fait abstraction de tout détail particulier à l'une ou à l'autre application, on peut résumer la fonction de la programmation avec la formule :

F(X) = Y

où :

  • X représente l'input
  • Y représente l'output
  • F() représente la fonction qui permet de transformer X en Y

Si on prend ce wiki (ou plus précisément MediaWiki, le logiciel qui permet de créer des wikis) en tant qu'exemple, cette formule peut être appliquée à plusieurs niveaux de granularité.

De manière générale/synoptique l'objectif (i.e., le besoin ou problème à résoudre) d'un wiki est de fournir un espace d'écriture collaboratif, voué principalement au partage de connaissances. Dans cette perspective on peut adapter la formule ainsi :

  • X représente toute forme de contenu produit par un utilisateur
  • Y représente l'ensemble des connaissances disponibles
  • F() représente la fonction qui permet de passer du contenu individuel d'un utilisateur à un système partagé de connaissances

En termes formels, on peut donc écrire ceci sous la forme

MediaWiki(contenu utilisateur) = partage connaissances

De manière plus spécifique, en tenant compte du contexte d'utilisation, on peut retravailler la formule de la manière suivante :

  • X représente tout contenu accepté par le Wiki (texte qui respecte la syntaxe, format de fichiers valables, ...)
  • Y représente la mise en forme et l'organisation des contenus en pages
  • F(X) représente l'ensemble d'instructions qui permet de passer de contenus textuels et multimédia ajoutés par les utilisateurs à un réseau de liens hypertextuels qui connectent les pages/contenus

En termes formels, la formule devient :

MediaWiki(texte avec syntaxe wiki + fichiers acceptés) = pages wiki

De manière encore plus détaillée, il y a des sous-fonctions qui contribuent à la fonction MediaWiki(), par exemple :

  • F(clique sur Accueil) = pointer vers la page d'accueil
  • F(texte modifié + enregistrement) = mettre à jour le contenu d'une page
  • F([[Catégorie:Programmation]]) = ajouter la page à la Catégorie:Programmation
  • Etc.

L'intention du développeur et le rôle de la machine

Programmer signifie traduire les intentions du développeur afin que la machine les exécute conformément aux attentes. Adaptation de Denning & Martell (2015, p. 90, Fig. 5-3)

Si on traduit F(X) = Y en termes intentionnels, programmer signifie communiquer à la machine les attentes du développeurs (et par extension des utilisateurs, même si ce passage n'est pas du tout automatique !) en termes de transformation de l'input en output.

La flexibilité du numérique fait ainsi que les trois composantes, X, Y et F(), soient transformées en code binaire et par conséquent que ce soit la machine, avec toute sa puissance computationnelle, à établir l'output désiré en fonction de l'input donné. Les progrès techniques des dernières années nous permettent aujourd'hui d'avoir :

  1. Différents périphériques pour encoder l'input tels que les "traditionnels" souris et clavier, mais également des écrans tactiles, senseurs, lecteurs optiques, caméra à haute définition, capteurs bio-métriques, etc.
  2. Une puissance computationnelle énorme que se soit au niveau des dispositifs eux-mêmes ou grâce à la communication avec le cloud-computing.
  3. Différents périphériques pour décoder l'output tels que les écrans, mais également les imprimantes 3D, découpeuses laser, machines pour la broderie numérique, les robots, etc.

En dépit de ces progrès, il ne faut cependant pas oublier que la computation demeure un mécanisme qui nécessite du temps, de l’énergie et un dispositif physique (i.e. hardware) qui exécute le code. La programmation consiste donc dans l'écriture d'algorithmes qui puissent être encodés de manière à ce que la machine les exécute. Denning & Martell (2015, p. 86) identifient deux parties dans ce processus :

  1. Formuler une solution à un problème et l'encoder pour un compilateur ou un interprète.
  2. Vérifier que la machine contrôlée par le programme arrive à résoudre le problème de manière conforme aux attentes des développeurs et/ou des utilisateurs.

En fonction des intentions des développeurs et du contexte d'utilisation, il y a des langages de programmation et des machines qui sont plus ou moins déjà prédisposés pour répondre aux attentes. En d'autres termes, il y a eu déjà en amont la méta-intention des créateurs du langage de programmation ou concepteurs du hardware de prendre en compte une utilisation potentielle. Malgré cela, lorsqu'un langage de programmation devient "à la mode" ou qu'un développeur le maîtrise à un bon niveau, il y a plutôt la tendance à utiliser le même langage pour différentes finalités.

Ce phénomène confirme le fait que le rôle ultime d'un langage de programmation réside dans l'instruction des machines. Néanmoins, cette conception ne prend pas en compte le rôle d'outil cognitif que le langage de programmation joue pour les intentions des développeurs. En d'autres termes, pour que le développeur puisse instruire la machine, il faut en amont qu'il soit capable de formuler les algorithmes (cf la pensée computationnelle plus haut dans la page). Dans ce sens, le rôle du langage de programmation est fondamental et dans la section suivante nous verrons les blocs constitutifs qui permettent la création d'instructions pour les machines.

Approche pragmatique à la programmation

Si on intègre les aspects conceptuels et techniques de la programmation, on peut obtenir une définition pragmatique telle que :

Un programme est une solution automatisée qui permet à des utilisateurs d’obtenir, à partir de certains inputs, des outputs qui contribuent à répondre à un besoin.

Cette définition peut s’adapter à tout type de programme: un algorithme qui permet de mieux réduire la taille d'un fichier audio sans perdre en qualité sonore, un logiciel de traitement de texte, un système d'exploitation ou encore une plateforme web comme Moodle. Dans cette approche pragmatique, nous considérons la programmation comme la phase d’implémentation d’une application destinée à d’autres utilisateurs. Cela se traduit par le fait qu'une partie des inputs utilisés par l'algorithme est déterminée par les utilisateurs eux-mêmes. Ce principe est à la base de l'interactivité : plus une application est interactive, plus les manipulations de l'utilisateur seront utilisées en tant qu'input pour déterminer l'output.

Anatomie d'une application interactive

Structure d'une application.

Dans une application interactive (un logiciel, une application web, une application pour smartphone, etc.), on peut souvent identifier trois composantes :

  • Une partie graphique
    L'interface utilisateur (Graphic User Interface, GUI) présente des éléments « visibles » sur l’interface et permet aux utilisateurs de fournir des inputs et/ou de recevoir les outputs du programme.
  • D’autres éléments
    Il s'agit de toutes sortes d'extension de l'application qui peuvent être utiles pour le programme, comme par exemple une base de données pour sauvegarder, récupérer, modifier ou effacer des données ; un espace de stockage pour sauvegarder les outputs du programme ; ou encore le web pour récupérer des informations en temps réel, etc.
  • Une partie logique
    Elle régit les entrées/sorties du programme en termes de causalité (e.g. si je clique sur le bouton A, il se passe B, si je clique sur le bouton C, il se passe D). La partie logique est normalement censée changer la partie graphique en présentant une évolution de l’état initial qui atteint la solution envisagée, ou au moins représente un état plus intéressant pour l’utilisateur. En même temps, elle sert d' « intermédiaire » entre le programme et les éléments externes, par exemple en indiquant à la base de données quelles données récupérer ou au système d'exploitation quel fichier sauvegarder.

Formellement, seule la partie logique est nécessaire à un programme (e.g. algorithme pour comprimer un fichier audio), mais dans une perspective interactive, les deux autres éléments sont souvent présents. L’interface graphique est censée faciliter l’interaction personne-machine (décrite plus haut dans la page), tandis que l’intégration avec d’autres éléments est souvent nécessaire pour « prolonger » les fonctionnalités du programme et atteindre ainsi les objectifs de l’utilisateur.

Ainsi, de manière plus concrète, programmer correspond à définir la partie logique d’une application pour qu’elle puisse mener à bien une ou plusieurs tâches qui contribuent à résoudre un problème ou à répondre à un besoin. Pour ce faire, il faut créer les algorithmes correspondant aux comportements attendus. Les langages de programmation diffèrent dans la manière de constituer les instructions contenues les algorithmes (i.e. écrire le code source) mais on retrouve souvent des éléments communs qui représentent des briques fondamentales de la programmation. Parmi ces éléments figurent :

  • Les variables
  • Les opérateurs
  • Les structures de contrôle
  • Les boucles
  • Les fonctions
  • Les tableaux (ou array)
  • Les objets
  • Les événements
  • Les expressions régulières

Les variables

Comme le nom l’explique, les variables sont des éléments qui sont censés changer à un moment donné dans la logique de l’application. En même temps, comme dans les formules mathématiques ou physiques (e.g. vitesse), elles permettent d’identifier une valeur avec un nom : chaque fois qu’on fait référence au nom de la variable, elle sera substituée dans l’algorithme par la valeur correspondante à ce moment dans la logique de l’application.

De manière métaphorique, on peut penser aux variables comme des boîtes avec des étiquettes. Les boîtes peuvent contenir un ou plusieurs éléments, et l'étiquette sert en tant que référence symbolique pour indiquer l'élément ou les éléments contenus dans la boîte. Une fois que la boîte a sa propre étiquette, on peut utiliser celle-ci pour faire référence à tous les éléments contenus. Ces éléments peuvent changer dans le temps, mais l'étiquette servira toujours pour référencer cette boîte en particulier. La métaphore est néanmoins limitée par les contraintes physiques des boîtes, tandis que pour les variables on peut avoir toute sorte de cas de figure : des variables qui sont utilisés comme des références à d'autres variables ; des variables qui sont contenues à l'intérieur d'autres variables ; etc..

Parmi les différentes utilités des variables, on peut en identifier deux principales :

  • Modifier la valeur associée à travers la logique de l’application
    C'est l'une des manières les plus utilisées pour garder une trace de l'évolution de l'application suite à son utilisation. Par exemple dans le cas du « score » dans un jeu, celui-ci est destiné à augmenter (ou diminuer) selon les objectifs atteints par le joueur ;
  • Réutiliser dans différents endroits du code source une valeur sans avoir à la répéter littéralement à chaque fois
    Il est plus pratique d'écrire le nom d'une variable tel que "PI" plutôt que saisir à chaque fois 3.141592653589793. Dans certains langages de programmation, on utilise dans ce contexte plutôt des constantes justement parce qu'elles ne sont pas censées varier. Une fois définie, une constante va garder la même valeur pendant l'exécution du code.

Dans la littérature technique, aux variables sont souvent associées les actions suivantes :

  • Déclaration d’une variable
    Cela correspond à créer une variable, c’est-à-dire à insérer pour la première fois une variable dans la logique de l’application.
  • Affectation/Assignation d’une variable
    Cela correspond à associer une valeur à la variable, ce qui se fait normalement grâce à un opérateur d’affectation, par exemple les symboles <-, , := ou =.

Selon le langage de programmation utilisé, il faut également définir à quel type de valeur/de données une variable va être associée. Parmi les types de données plus fréquentes, on identifie :

  • Les suites de caractères (ou chaînes, string) : elles sont utilisées pour représenter du texte (des mots, des phrases, etc.) ;
  • Les chiffres (nombre entier, à virgule flottante, etc.) : ils sont utilisés surtout avec des opérateurs mathématiques ;
  • Les valeurs booléennes ( en anglais : booleans)  : elles sont des valeurs dichotomiques (soit vrai, soit faux) ;
  • Les tableaux (array) : ils sont utilisés pour créer des listes avec des index qui permettent de récupérer la valeur associée ;
  • Les objets : ils sont des conteneurs qui peuvent inclure souvent tout type de données, y compris des sous-objets, des variables (c'est-à-dire des propriétés), ou des fonctions (appelées méthodes dans ce cas)

Le type de variable est important, car il détermine les types de manipulations qu’on peut faire. Par exemple, dans le cas d’une variable qui contient un nombre entier, on peut effectuer des opérations mathématiques ; pour une suite de caractères, on peut transformer le texte (mettre tout en majuscules, remplacer certains mots, etc.) ; pour un tableau, ajouter ou enlever des éléments de la liste ; etc.

Les opérateurs

Les opérateurs permettent de manipuler ou comparer des valeurs, notamment des variables. Parmi les opérateurs utilisés fréquemment dans tout langage de programmation on identifie :

  • Les opérateurs mathématiques : addition, soustraction, multiplication, division, etc.
  • Les opérateurs de comparaison (égalité, différence, plus grand ou plus petit)
  • Les opérateurs logiques (AND et OR)

Les structures de contrôle

Les structures de contrôle permettent d’exécuter seulement certaines instructions d’un programme selon la vérification d’une ou plusieurs conditions. La version sémantique la plus répandue des structures de contrôle équivaut à l’inférence logique « si… alors… ». Le type de mécanisme de contrôle peut concerner différents niveaux de granularité. Par exemple :

  • Si la note d'un examen est inférieure à 4, alors la note est insuffisante ;
  • Si le joueur n'a pas débloqué une porte, alors il ne peut pas accéder au niveau supérieur ;
  • Si le mot de passe choisi contient moins de 6 caractères, alors il est trop court ;
  • Si l'extension du fichier n'est pas .pdf, alors ne le téléverse pas sur le serveur ;
  • Etc.

Les structures de contrôle sont parmi les éléments les plus utilisés dans la création des algorithmes car elles permettent d’adapter le comportement de l’application à certaines conditions qui ont été prévues à l’avance, pour que le programme puisse fournir un output adapté. De plus, elles peuvent déterminer également l'exécution (ou pas) de certaines instructions selon le schéma :

  • Si cette condition s'avère, alors...
    Exécute l'instruction n°1
    Exécute l'instruction n°2
    Exécute l'instruction n°9
  • Sinon...
    Exécute l'instruction n°7
    Exécute l'instruction n°10

Les structures de contrôles sont assez similaires aux événements (voir plus bas), mais leur finalité est généralement plus restreinte. Elles s'occupent surtout d'évaluer des conditions dans l'état "interne" de l'application ou des données, plutôt que de répondre à des stimulations "externes".

Les boucles

Les boucles sont à la base d’un concept très utile en programmation : l'itération. L'itération permet d’exécuter de manière récursive une ou plusieurs opérations (les mêmes ou différentes) à tous les éléments qui font partie de la liste itérée. Les boucles sont par exemple très utiles en relation avec les tableaux ou les bases de données, car elles permettent d’effectuer des opérations sur tous les éléments contenus dans une liste (e.g. pour chaque étudiant présent dans un cours, on peut afficher son nom et récupérer la moyenne de ses notes de manière automatisée). Les boucles sont également utilisées pour créer des éléments de manière récursive, ce qui peut être utile par exemple dans un jeu qui propose un nombre d'obstacles proportionnel au niveau du joueur. On peut créer une boucle qui disperse des obstacles selon le rapport niveau de jeux * 2. À ce moment, la boucle va faire une itération 2 fois pour le premier niveaux, 4 fois pour le deuxième, etc.

De manière sémantique, une boucle correspond à instruire le programme de faire quelque chose jusqu’à ce qu’une condition ne s’avère pas. Dans l'exemple précédent, cette condition équivaut à donner l'instruction :

  1. Crée des obstacles et disperse-les jusqu'à ce qu'on en a deux fois le niveau actuel.
  2. Lorsqu'on a atteint ce nombre d'obstacle, arrête de créer des obstacles.

En effet, une des caractéristiques fondamentales des boucles est qu’elles doivent se terminer à un moment donné dans la logique de l’application. Pour ce faire, on utilise très souvent une variable (voir plus haut) qui mémorise une valeur et met à jour cette valeur après chaque itération. La boucle évalue donc au prochain tour si cette nouvelle valeur respecte encore le critère de vérification. Si elle le fait, alors la boucle continue ; si non, la boucle s'arrête.

  • Exécute les instructions suivantes jusqu'à ce que le nombre d'obstacles créés est mineur ou égale à 16
    Crée l'obstacle
    Augmente le nombre d'obstacles crées d'une unité

Après la 16ème itération, le nombre d'obstacles créés ne sera plus mineur ou égale à 16 et par conséquent la boucle s'arrêtera.

Les fonctions

Les fonctions représentent une sorte de "programme dans le programme", car elles sont la première forme d'organisation du code. On utilise des fonctions pour regrouper des instructions et les appeler sur demande : chaque fois qu'on a besoin de ces instructions, il suffira d'appeler la fonction au lieu de répéter toutes les instructions. Pour accomplir ce rôle, le cycle de vie d'une fonction se divise en deux :

  1. Une phase unique dans laquelle la fonction est déclarée (créée)
    On définit à ce stade toutes les instructions qui doivent être groupées pour obtenir le résultat souhaité ;
  2. Une phase qui peut être répétée une ou plusieurs fois dans laquelle la fonction est exécutée (appelée)
    On demande à la fonction de mener à bien toutes les instructions qu'elle contient à un moment donné dans la logique de notre application.

On peut imaginer d'utiliser une fonction dans le jeu morpion (i.e. Tic-tac-toe). Cette fonction sera appelée chaque fois que l'utilisateur ajoute un nouveau symbole dans une case. À minima cette fonction devrait exécuter les instructions suivantes :

  1. Contrôler que la case n'est pas déjà occupée
    • Si elle est occupée, demander un nouveau input et recommencer au point 1
  2. Contrôler que le symbole appliqué soit différent du précédent (pour garantir l'alternance)
    • Si c'est le même, demander un nouveau input et recommencer au point 1
  3. Contrôler si, suite à l'ajout de ce symbole, un trio a été formé dans l'une des directions permises par les règles du jeu
    • Si un trio est créé, alors le jeu s'arrête et le joueur gagne le point
    • Si aucun trio a été créé, alors le jeu continue
  4. Contrôler s'il reste des cases vides
    • S'il reste au moins une case vide, demander un nouvel input et recommencer au point 1
    • Si non, le jeu s'arrête

Si on utilise une notation formelle pour exprimer cette logique, on peut identifier la fonction qu'on vient de créer avec le symbole générique F() et lui passer un argument qui correspond au numéro de la case de 1 à 9 en tant qu'input X pour obtenir l'un des outputs possibles de notre action, notée Y. Donc on aura :

F(X) = Y

qui peut, selon la dynamique du jeu, prendre les valeurs suivantes :

  • F(1) = Y
  • F(2) = Y
  • F(3) = Y
  • etc.

et générer selon la logique interne de la fonction un output Y correspondant respectivement à :

  1. Continuer l'alternance du jeu
  2. Arrêter car l'un des joueurs à gagné
  3. Arrêter car il n'y a plus de cases disponibles

Les tableaux (array)

Les tableaux (ou array en anglais) sont des listes indexées d'éléments qui partagent normalement une certaine relation sémantique pour appartenir à la liste. Un exemple tout simple d'array est la liste des courses : on peut indexer cette liste en fonction de l'ordre des articles à acheter :

  1. Lait
  2. Farine
  3. Pommes
  4. Fromage

Par la suite, on peut se référer aux éléments dans notre liste avec l'index. Par exemple, vous pouvez barrer le deuxième élément de votre liste lorsque vous avez mis de la farine dans votre panier, ou ajouter un cinquième élément parce que vous avez besoin de quelque chose d'autre. Veuillez noter que dans la plupart des langages informatiques, on commence à compter à partir de zéro, donc l'index du lait serait 0, de la farine 1, etc.

La plupart des éléments d'un système informatique qui s'occupent du stockage de données (Base de données, gestionnaire de fichiers, etc.) sont en quelque sorte une extension du concept de array, car les items individuels stockés peuvent être récupérés à partir d'un index. La flexibilité de ce principe permet d'associer à un index toute sorte de valeurs, et de pouvoir les manier (récupérer, modifier, effacer, etc) par la suite à travers la référence à cet index.

Les objets

On dit souvent en informatique qu'un langage est "orienté aux objets". Un objet en programmation est un élément qui possède des caractéristiques (appelées propriétés) et qui peut normalement exécuter certaines fonctions (appelées méthodes). On peut imaginer des objets par exemple dans un jeu comme Super Mario Bros. Les différents ennemis qu'il faut éviter pendant le jeu sont des objets, et selon le type d'ennemi (c'est-à-dire le type d'objet), ils varient en fonction :

  • De leur caractéristiques (propriétés) : la couleur, la forme, les coups nécessaires pour les faire disparaître, etc.
  • Des actions (méthodes) qu'ils peuvent effectuer: sauter, lancer des objets, etc.

Chaque fois qu'un ennemi (objet) d'un certain type se présente à l'écran, il est une instance du type d'ennemi auquel il appartient. En tant qu'instance, il partage avec les autres ennemis de son type les mêmes propriétés et méthodes, mais il est en même temps unique, car lorsque vous le tuez, c'est seulement lui (et pas tous les ennemis de ce type) qui va disparaître.

Pour rester sur le même exemple, on peut identifier Mario et Luigi comme appartenant au même type d'objet (e.g. personnage principal) : ils partagent les mêmes méthodes (sauter, courir, ...) mais ils sont différents dans certaines propriétés (couleur de la casquette, etc.).

Les événements

On parle d’événement lorsque la logique de l’application prévoit l’exécution de certaines instructions qui sont liées à une modification de l’état actuel du programme. Les événements peuvent être liés à plusieurs aspects, comme par exemple :

  • Les événements liés aux manipulations de l’utilisateur
    Cliquer sur le bouton de la souris, sur la touche du clavier, déposer un objet après l'avoir glissé sur une cible, ...
  • Les événements liés au temps
    La disparition d’un message après 10 secondes, le temps limite pour terminer un QCM, la mise à jour d'un graphique chaque 5 minutes, etc.
  • Les événements liés aux ressources internes ou externes à l’application
    L'établissement d'une connexion à une base de données, la fin du téléchargement d’une image, le démarrage de la lecture d'une vidéo, etc.

Les événements sont à la base d'un type de programmation qu'on appelle asynchrone, car le code ne sera pas exécuté progressivement, dès que l'interprète du langage le lit. Il sera plutôt exécuté dans un deuxième temps, un temps que souvent nous ne pouvons pas prévoir à l'avance :

  • À quel moment précis un utilisateur va cliquer sur un bouton dépend de la décision de la personne, sa dextérité, le temps de réaction pour identifier et pointer le bouton, etc.
  • À quel moment précis se termine le téléchargement d'une image dépend de la réactivité du serveur qui la héberge, de la qualité de la connexion, etc.

Pour cette raison, on crée des gestionnaires d'événements qui s'occupent de :

  1. Définir quel sera l'événement déclencheur
    Il s'agit de manière générale d'une "nouveauté" par rapport à la situation actuelle. Un changement qui se produit et qui peut être déterminé, comme on l'a vu plus haut, par différentes sources.
  2. Exécuter les instructions souhaitées suite au déclenchement de l'événement
    C'est la règle de comportement de l'application qui s'occupe de réagir à la "nouveauté" en fonction des intentions du programme/développeur. Le même événement peut avoir toujours la même réaction ou avoir une réaction différente en fonction d'autres éléments contextuels à l'état de l'application. Imaginez un jeu point-and-shoot qui lance un projectile chaque fois qu'on appuie sur la barre espace du clavier, à condition qu'on ne les ait pas épuisés.

Les expressions régulières

Parmi les nombreuses difficultés qu'on peut rencontrer dans la programmation, les expressions régulières sont parmi les plus compliquées à maîtriser. Néanmoins, elles sont fondamentales au fonctionnement de plusieurs concepts informatiques, y compris les langages de programmation eux-mêmes. Les expressions régulières permettent en effet d'identifier des patterns à l'intérieur d'une source d'information de référence (par exemple un texte).

Grâce a des règles d'identification très complexes, on arrive à identifier des éléments comme appartenant au même pattern, même avec des degrés de différences importants. Par exemple il existe des expressions régulières qui permettent d'identifier si une suite de caractères représente ou pas un URL valide indépendamment du fait que le texte propose :

  • Le protocole http:// ou https://, ou pas de protocole du tout
  • Le format www.domain.extension, seulement le format domain.extension, ou un ou plusieurs sous-domains (e.g. tecfa.unige.ch)
  • Un, plusieurs ou aucun chemin (i.e. path) après le domain (e.g. tecfa.unige.ch/go/to/some/specific/directory)

Conclusion

Dans cette page nous avons proposé trois approches différentes, mais complémentaires, à la programmation :

  • L'approche conceptuelle suggère que la programmation est tout d'abord une activité mentale, une manière de penser, qui consiste à computer une solution en décomposant un problème en plusieurs étapes ;
  • L'approche technique identifie la programmation en tant que trait d'union entre le langage humain et le langage des machines. Cette traduction se fait à l'aide d'algorithmes qui déterminent comment un programme doit gérer/transformer des données de input en données de output ;
  • L'approche pragmatique combine les approches conceptuelles et technique en identifiant des éléments communs à la plupart des langages/techniques de programmation : les blocs constitutifs des algorithmes qui permettent de fournir des outputs satisfaisants par rapport aux inputs fournis.

La perspective qui permet de passer d'une représentation abstraite de la programmation à une utilisation concrète, telle qu'un logiciel ou un jeu multi-utilisateurs, consiste à s'imaginer une grande quantité d'instructions (regroupées dans des algorithmes) qui prennent en compte les utilisateurs eux-mêmes (à travers des actions/inputs). Lorsqu'on comprend les éléments constitutifs de la programmation (variables, boucles, etc.), le développement se réduit à deux aspects :

  1. Gérer la complexité
    Savoir s'orienter dans le labyrinthe du code source et savoir identifier les "intentions" de chaque bout de code : comment et pourquoi ces instructions contribuent à la solution du problème ;
  2. Planifier des objectifs atteignables
    Disposer d'assez de temps et de connaissances pour traduire en code tous les éléments constitutifs du programme (logique, interface, sauvegarde des données, etc.).
En fin de compte, le code source d'un programme ou d'une application représente la manière ultime de répondre à un besoin.

Ressources

Scratch

Développé par le MIT, Scratch permet d'appliquer les aspects fondamentaux de la programmation à travers une interface drag&drop. Pour sa simplicité, il est souvent utilisé pour introduire les néophytes de tout âge à la programmation. Essayer un tutoriel de Scratch peut être une bonne manière pour consolider les aspects techniques illustrés dans cette page.

JavaScript

Ce wiki propose un parcours d'apprentissage de JavaScript à travers la pensée computationnelle en sciences sociales. Le parcours est destiné à tout public, surtout sans un background informatique préalable. Pour plus de détails, voir :

R

Ce wiki propose un parcours d'apprentissage du langage de programmation R et de son écosystème à travers la pensée computationnelle en sciences sociales. R est une solution de plus en plus utilisée pour le traitement de données, que ce soit dans la recherche ou dans l'enseignement. Pour plus de détails, voir :

Programming Boty

Programming Boty est un kit de construction destiné à proposer une initiation à la programmation informatique afin d'initier à la pensée / la logique informatique. Il a été développé dans le cadre du cours STIC III (2016) par Lydie Boufflers et Sophie Linh Quang. Il est disponible sur demande dans les locaux du TECFA.

Cours Online

Beaucoup de cours proposés en ligne (i.e. MOOCs) sont liés à l'informatique (Computer Science en anglais). On peut trouver des cours d'introduction ainsi que des cours axés sur des technologies/langages plus spécifiques.

Introduction à la programmation

MOOC sur Coursera créé par Laurent Moccozet et Alexandre De Masi de l'Université de Genève. Depuis le descriptif du cours sur la plateforme :

« Quel que soit votre domaine d’études, vos activités professionnelles futures vous confronteront régulièrement aux technologies numériques. Même si ces technologies sont rendues aussi intuitives que possible, il est souvent nécessaire de les configurer ou de les adapter pour pouvoir les exploiter de façon optimale. Des compétences de base de la pensée informatique et de la programmation vous permettront de franchir ces étapes de façon appropriée. L’initiation à la programmation permet concrètement de s’ouvrir à la pensée informatique et à la mettre en pratique. »

Nand2Tetris

L'un des cours qui a eu un certain succès est le projet Nand2Tetris créé par Noam Nisan et Shimon Schocken dans lequel il est possible de créer, en simulant des éléments physiques séparés, un ordinateur fonctionnel et de le programmer pour pouvoir enfin jouer au jeu Tetris. Le projet est divisé en deux cours qui sont disponibles sur la plateforme Coursera. Le premier cours s'intéresse à la partie hardware, tandis que le deuxième est consacré à la partie software. Le projet est également lié à un livre "The Element of Computing System", MIT Press, et tous le matériel est disponible sous licence open-source avec la possibilité de le modifier dans des contextes éducationnels non-profit.

W3Cx

Le World Wide Web Consortium (W3C), organisme qui s'occupe des standard web, organise en partenariat avec la plateforme MOOC EdX des cours portant sur HTML5, CSS et JavaScript.

Introduction to Computational Thinking

Cours proposé par le MIT et qui utilise le langage de programmation Julia pour introduire la pensée computationnelle :

Liste de langages de programmation

Voici une liste arbitraire et non exhaustive des langages les plus utilisés, avec une petite description. Pour une liste complète voir la liste sur Wikipedia :

  • ActionScript : utilisé dans les applications qui utilisent Adobe Flash
  • ASP.NET : langage propriétaire de Microsoft, utilisé pour le développement d’application web
  • C : langage pour la programmation système, il existe des « évolutions » telles que C++ ou C#
  • Java : langage dont l’objectif principale est la portabilité entre plateforme
  • JavaScript : langage utilisé surtout dans la programmation web côté client et dans Node.js
  • Julia : langage pensé principalement pour l'utilisation dans des sciences computationnelles
  • Objective-C : langage utilisé surtout dans l’environnement Apple
  • Perl : langage utilisé surtout en relation avec des fichiers de texte
  • PHP : langage open-source très utilisé dans le développement de site web
  • Python : langage utilisé souvent (mais pas seulement) dans les milieux académiques
  • R : langage utilisé surtout en relation avec le traitement statistique des données
  • Scheme : langage de programmation fonctionnel dérivé du langage Lisp
  • Visual Basic : langage utilisé dans l’environnement Microsoft
  • XSLT : langage de transformation de XML

La Free Ebook Foundation propose sur GitHub une liste de livres numériques gratuits sur différents langages de programmation. Il existe des listes dans plusieurs langues, la plus fournie est en anglais, mais il existe aussi une liste en français.

Bibliographie

  • Denning, P. J. (2016). Remaining Trouble Spots With Computational Thinking. Cacm, 60, 33–39.
  • Denning, P. J., & Martell, C. H. (2015). Great Principles of Computing. Cambridge, MA: MIT press.
  • Lieberman, H., Paterno, F., and Wulf, V. (eds.) 2006. End User Development. Springer.
  • McConnell, S. (2004). Code Complete. A practical handbook of software construction (2nd ed.). Redmond, WA: Microsoft Press.
  • Guzdial, M. (2015). Learner-Centered Design of Computing Education. Research on Computing for Everyone. Morgan & Claypool.
  • Petzold, C. (2000). Code. The Hidden Language of Computer Hardware and Software. Redmond, WA: Microsoft Press.
  • Tedre, M., & Denning, P. J. (2016). The Long Quest for Computational Thinking. In Koli Calling Conference on Computing Education Research (pp. 120–129)
  • Wing, J. M. (2006). Computational thinking. Communications of the ACM, 49, 33–35