Luca Saiu: page officielle du cours «Introduction à la programmation»

Cette année je suis responsable du cours M02 Introduction à la programmation pour la License Pro ASUR en formation continue du Département Réseaux/Télécommunications de l'IUT de Villetaneuse.
Le cours est une première introduction, et utilise le langage Python.

Cette page est la page web officielle du cours.

Évaluation

Votre note sera déterminée par le projet, et en mesure mineure par votre participation et interaction pendant le cours.

J'ai préparé une page séparée pour le projet, où tout détail est expliqué.

Matériel du cours

La version originale de ce matériel a été rédigée par Camille Coti, que je remercie pour son travail excellent.

Exemples et code source Python

Le code que j'ai écrit sur l'ordinatur pendant les cours (exemples simples et moins simples un peu mélangés, sans beaucoup de commentaires) :

Fichiers que j'ai généré avec un script, contenant la définition d'une liste words, pour le TP. Chaque listes est très longue, obtenue en prenant le texte complet d'un roman après avoir éliminé la ponctuation.

Ces fichiers contiennent des mots dans les mêmes langues, groupés en échantillons plus pétits. Chaque fichier a une seule définition en Python de la variable sample_words ;

Répertoire contenant tout fichier, y compris les textes originaux :

Précisions, corrections et intégrations

Cette section contient des différences importantes entre la version papier des slides que j'ai donné aux étudiants le premier jour et la version corrigée que vous trouvez ici. Ce n'est pas nécessaire de réimprimer les slides — et surtout n'utilisez pas les imprimantes publiques de l'IUT pour ça, s'il vous plaît.

écrit le 28 octobre, même si les étudiants sont déjà bien informés

J'ai coupé brutalement le programme pour le rendre moins ambitieux, mais plus réaliste. Le projet est aussi très simplifié par rapport à mon idée initiale.

21 octobre: paramètres formels, paramètres actuels, call by value

Pendant le dernier TP j'ai introduit explicitement les notions de:

Les langages comme Python (et la majorité des autres : C, C++, Pascal, PHP, Java, Lisp, ML, Smalltalk, Ada, Ruby, Perl ...) où les paramètres actuels sont complètement évalués et réduits à valeurs avant d'exécuter le corps d'une fonction sont dits «langages call-by-value». C'est la stratégie plus commune, mais pas la seule possible.

Un peu de terminologie utile, à mon avis, pour parler de langages d'une façon plus précise.

21 octobre: méthodes

La version papier des slides décrivait certains opérations sur les collections génériquement comme «fonctions», quand en fait elles sont des méthodes. Je ne vais pas expliquer en détail la programmation à objets et le rôle des méthodes, mais en pratique c'est indispensable de connaître au moins la différence syntaxique entre l'appel d'une méthode

objet.nomdelaméthode(paramètres actuels)
par rapport à l'appel d'une fonction
nomdelafonction(paramètres actuels)

Malheureusement on ne peut pas ignorer cette différence : en essayant d'utiliser la syntaxe de l'appel de fonction avec une méthode

nomdelaméthode(object, paramètres actuels)
on aura une erreur.

21 octobre: sharing et shallow copy

J'ai introduit la différence entre le sharing (appelé "shallow copying" dans la version papier des slides) et le vrai shallow copying :

La différence entre sharing et shallow copying est importante en pratique si l'on travaille avec des structures complexes. C'est conceptuellement important, et utile pour raisonner sur le comportement exact des programmes et sur l'efficacité des opérations.

21 octobre: destructivité

On appelle «opérations destructives» les opérations sur une collection qui modifient la structure originale, au lieu d'en créer une autre à nouveau.

Par exemple la concaténation de listes avec l'opérateur + n'est pas destructive :

>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> a + b
[1, 2, 3, 4, 5, 6]

L'opérateur + a construit une nouvelle liste. Le résultat partage les éléments des deux listes données, mais les deux listes n'ont pas été modifiées :

>>> a
[1, 2, 3]
>>> b
[4, 5, 6]

Par contre, la majorité des opérations sur les collections en Python sont destructives :

>>> a.append (57)

La méthode append n'a renvoyé aucun résultat ; on peut donc imaginer que la liste originale ait été modifiée. C'est en fait le cas :

>>> a
[1, 2, 3, 57]

En fait on a perdu la version originale de la liste qui avait trois éléments. La liste existe encore, mais elle est maintenant différente.

Terminologie: certains auteurs appellent les opérations destructives «in-place», et les opérations non destructives «not-in-place» ou «out-of-place».

15 octobre: en Python les valeurs sont typées (dynamiquement); les variables ne sont pas typées du tout

La fonction type, comme toute fonction en Python, travaille sur les valeurs qu'elle reçoit en tant que paramètres et pas sur les variables liées à ces valeurs, qui peut-être n'existent même pas ; par exemple type(2 + 5) est une expression parfaitement correcte et raisonnable, et son paramètre n'est pas une variable : c'est l'expression 2 + 5, qui n'est pas (ni contient) une variable.

Dans notre pseudo-code on a des variables typées, comme dans certains vrais langages de programmation dits langages typés statiquement (par exemple : C, C++, Java, Pascal, Fortran, Ada, ML, Haskell) ; Python, par contre, est un langage typé dynamiquement (autres exemples : Lisp, Prolog, PHP, Ruby, Smalltalk). Dans un langage typé dynamiquement les variables n'ont pas de type associé : juste les valeurs ont un type.

C'est utile de raisonner sur des variables typées dans notre tête et quand l'on écrit du pseudo-code, particulièrement pour des débutants : mais si l'on utilise Python le typage des variables est juste un modèle mentale, que Python n'oblige pas de respecter.

Certains experts préfèrent les langages typés dynamiquement, certains autres les langages typés statiquement. Presque sûrement il n'existe pas de solution meilleure en tout cas : chacun des deux modèles a des mérites. Des solutions intermédiaires sont aussi possibles. (Le typage dynamique est souvent un avantage pour les experts, mais peut être un obstacle pour les débutants.)


Précisions, corrections et intégrations mineures ou avancées

Je note ici certains points plus avancés, qui sont hors sujet dans notre cours introductif. Les étudiants ne sont pas tenus à retenir les informations dans cette section. Quand même, les questions sont intéressantes et quelques-uns pourraient être encouragés à approfondir.

21 octobre: expressivité et équivalence entre boucles

Je note en retard une remarque que j'ai fait oralement pendant le premier cours.

La version papier des slides parle génériquement d'«équivalence» entre boucles mais les boucles structurés présentées dans notre pseudo-code ne sont pas toutes équivalentes entre elles.

Comment j'ai montré très informellement au tableau toute boucle pour peut être systématiquement réécrite en utilisant une boucle tant que .. faire, mais le contraire est faux (un exemple : les boucles infinies). La boucle tant que .. faire et la boucle faire .. tant que sont équivalentes entre elles : on peut systématiquement réécrite l'une en utilisant l'autre (si on a les tests).

On peut prouver formellement ces différences en puissance ou expressivité, mais c'est hors sujet pour ce cours. On peut étudier ces détails dans des cours plus théoriques sur la théorie de la calculabilité.

Ici nous avons un autre bout : nous utilisons le pseudo-code pour décrire de façon claire nos algorithmes, avant d'écrire du code. Une expression compacte et explicite de l'algorithme est bien plus importante que la minimalité : on ne cherche donc pas nécessairement de minimiser les formes possibles de boucles, car l'intention du programmer peut être mieux exprimée en utilisant une boucle moins générale et donc moins «puissante» qui respecte un motif typique : par exemple c'est bien plus claire d'utiliser une boucle pour si on veut itérer sur une séquence d'indices entiers consécutifs, même si utiliser systématiquement faire .. tant que comme la seule forme de boucle serait suffisant pour exprimer tout procès de calcul.


Réponses aux questions et suggestions pratiques

Quelques suggestions techniques et réponses aux questions des étudiants.

20 octobre: input, raw_input et Python 3

Un étudiant aimerait utiliser Python 3, mais sans être empêché par les changements incompatibles qui ont été introduits après la version 2. En particulier l'étudiant souhaiterait garder l'ancien comportement des fonctions input et raw_input.

Une solution possible consiste à exécuter ce morceau de code que j'ai écrit ; le code marche avec toute version de Python — il ne fait rien sur Python 2, mais il n'échoue pas.

import sys
# Comparaison entre deux nuplets : ordre lexcographique
if sys.version_info >= (3, 0):
  default_input = input
  # Simulation de l'ancienne fonction «raw_input» de Python 2:
  def raw_input (prompt = ""):
    return default_input (prompt)
  # Simulation de l'ancienne fonction «input» de Python 2:
  def input (prompt = ""):
    return eval (default_input (prompt))

(Oui, il y a sûrement des solutions meilleures, mais ce code au moins est à la portée des étudiants.)

Il faut exécuter le code une seule fois, par exemple en le mettant tout au début du programme. Après son exécution input et raw_input auront le comportement de Python 2.



[hacker emblem]

Luca Saiu — IUT de Villetaneuse, Département Réseaux et Télécommunications
Mis à jour le 29 octobre 2015.