Ce cours, étant en partie intensif, en partie à distance et en partie
intercalé avec votre stage, suivra un format atypique. Ceci explique que les dates
effectives des séances en classe restent (au moins en partie) à déterminer, et
que j'aie choisi d'identifier les séances par Sxx
(pour « séance xx ») plutôt que par
Txx (théorie) ou Lxx
(laboratoire).
De manière générale, vos besoins guideront en partie notre démarche et nos
choix de contenu. Les problèmes (techniques) que vous rencontrerez en stage
pourront être discutés en classe, et nous chercherons à leur apporter des
solutions à la fois élégantes et efficaces.
Certaines de nos séances seront virtuelles (les dates sur le calendrier
ci-dessous seront identifiées s/o, donc sans objet).
Elles seront constituées d'une mise en situation (idéalement en personne, lors
de nos séances in vivo) puis de travail pratique lorsque votre horaire
le permettra, alimenté par des échanges électroniques. Lorsque nous nous
reverrons, nous mettrons en commun nos approches, nos questionnements, et nous
présenterons la matière au prochain défi. J'ai calculé l'équivalent de
30 séances, soit 15 séances « théoriques » et
15 séances « pratiques », mais nos séances seront pour l'essentiel des
hybrides. Bien que le cours soit siglé 2-2-2, nous
aurons des séances de format variable, tout en essayant de nous en tenir aux
60 heures prévues (incluant le volet « à distance »).
Date |
Séance |
Détails |
7 janvier A : 8 h 30-12 h B : 13h-16 h 30
|
S00 |
Cours au P-116. Au menu :
- Présentation du cours et du plan
de cours
- Petits exercices de révision
(solutions)(gestion,
industrielle)
- Notez que les deux sont semblables, outre quelques questions
distinctes vers la fin
- Discussions en classe en lien avec les réponses proposées par les
étudiant(e)s et celles suggérées par l'enseignant
- Réalisation de quelques exercices de base en laboratoire (description
des exercices, code à
partir duquel les réaliser)
- Discussions en classe en lien avec les réponses proposées par les
étudiant(e)s et celles suggérées par l'enseignant
|
8 janvier
B : 8 h 30-12 h A : 13h-16 h 30 |
S01 |
Cours au P-116. Au menu :
Dans les notes de cours :
À faire pour la séance S02, seul(e) ou en équipe de deux :
- Améliorer la classe ListeEntiers pour que :
- La méthode taille() soit de complexité
- La méthode ajouter() soit de complexité
- Le constructeur de copie et l'affectation deviennent de complexité
- Vous assurer que les attributs d'instance que vous aurez ajouté pour y
arriver soient tenus à jour de manière cohérente dans l'ensemble des
méthodes de ListeEntiers
- Vous aurez droit à un petit bonus si vous ajustez
inverser() pour qu'elle n'ait plus à faire d'allocation dynamique de
mémoire
- Remettez votre code dans un seul fichier .cpp
par équipe, avec les noms des équipières et des équipiers à la fois
dans le nom du .cpp et dans les commentaires au
début du fichier
Attention, vous avez peu de temps (surtout dans le cas du groupe A!)
alors utilisez votre temps avec intelligence!
|
9 janvier
A : 8 h 30-12 h B : 13h-16 h 30
|
S02 |
Cours au P-116. Au menu :
On se met ensuite au boulot...
- Exercice de rédaction d'une Liste<T>,
représentant une liste simplement chaînée générique, en
Java ou en
C# (à votre choix)
- Peu importe que votre code de Liste<T>
soit écrit en
Java ou en
C#, cette classe doit offrir au minimum les services suivants :
- constructeur par défaut, qui crée une liste vide
- mécanisme pour dupliquer une liste, donc créer une nouvelle liste
distincte de l'originale mais équivalente à cette dernière
- deux listes a et
b sont équivalentes si elles ont le même nombre de noeuds, et
si les noeuds de ces deux listes ont les mêmes valeurs dans le même
ordre
- mécanisme permettant d'inverser les éléments d'une liste
- mécanisme permettant d'ajouter un élément à une extrémité de la
liste
- mécanisme permettant d'extraire un élément à l'autre extrémité de la
liste
- mécanisme permettant de connaître le nombre d'éléments dans la
liste
- mécanisme permettant de tester la liste pour savoir si elle est
vide ou non
- mécanisme pour comparer deux listes et savoir si elles sont
équivalentes
- mécanisme pour afficher les éléments d'une liste sur un flux
- mécanisme pour vider une liste
- Consignes d'ordre général :
- visez à respecter les
idiomes de votre langage (si vous faites du
Java, faut que ça respecte les pratiques du code
Java; si vous faites du
C#, faut que ça respecte les pratiques du code
C#)
- utilisez les
exceptions
de manière judicieuse
- évitez les fuites de ressources
- Il faut que votre programme de test fasse au minimum les opérations
suivantes à l'aide d'instances de votre type
Liste<T> :
- tester chacun des services ci-dessus
- valider que si une liste est une copie d'une autre liste, les deux
peuvent être modifiées indépendamment l'une de l'autre
La classe Liste<T> est à remettre au début de la séance S04.
|
10 janvier
B : 8 h 30-12 h A : 13h-16 h 30 |
S03 |
Cours au P-116. Au menu :
- Échanges en classe sur les itérateurs
et sur les foncteurs,
de même que sur les λ,
pour voir des manières contemporaines de solutionner certains des
exercices
-
Programmer par algorithmes
-
Exercices formatifs d'utilisation d'algorithmes standards
-
Survol des
itérateurs
pour accéder à un flux
- Implémenter quelques algorithmes maison :
- l'algorithme inverser(debut,fin) qui
inverse l'ordre des éléments dans la séquence . Testez votre
implémentation avec un tableau de float, un vector<int>
et une
list<string>. Votre algorithme doit fonctionner avec une
séquence vide,
et doit donner les bons résultats que le nombre d'éléments y soit pair
ou impair. Vous avez le droit d'utiliser
std::swap()
- l'algorithme trouver(debut,fin,val) qui retourne un itérateur sur le
premier élément de la séquence qui soit égal à val
au sens
de l'opérateur ==. Testez votre implémentation avec
des itérateurs sur un vector<int> et
une list<string>. Si aucun élément n'est trouvé, retournez fin
- l'algorithme trouver_si(debut,fin,pred) qui retourne un itérateur
sur le premier élément e de la séquence
tel que pred(e) soit vrai. Testez votre
implémentation avec des itérateurs sur un vector<int>
et
une list<string>. Si aucun élément n'est trouvé, retournez fin
- Pour celles et ceux qui auront terminé, amusez-vous à implémenter :
- l'algorithme pivoter_gauche(debut, fin) qui pivote tous les éléments de la séquence
d'une position vers la gauche (donc une séquence contenant initialement
contiendra en fin de parcours
- l'algorithme pivoter_droite(debut, fin) qui pivote tous les éléments de la séquence
d'une position vers la droite (donc une séquence contenant initialement
contiendra en fin de parcours
Notez que ces algorithmes sont pour la plupart implémentés dans
<algorithm>, mais l'idée de ces exercices est d'essayer de les implémenter
vous-mêmes; lorsque vous les aurez implémentés, vous pourrez comparer
votre solution avec celle livrée par le standard si le coeur vous en dit.
Dans les notes de cours :
- Les foncteurs
sont décrits dans POO – Volume
02, pp. 152-173
- Les λ
sont décrites dans POO – Volume
02, pp. 179-190
- Les algorithmes standards sont survolés dans POO
– Volume 02, pp. 87-94
|
A : 14 janvier
13h-16 h 30 B : 15 janvier 13h-16 h 30 |
S04 |
Cours au P-116. Au menu :
Remise de votre classe Liste<T>, au début de la séance S04,
en format imprimé.
- Comparatif
Comparatif
d'implémentations
Java et
C# d'une ListeEntiers
- caractéristiques de l'implémentation
Java
- caractéristiques de l'implémentation
C#
- Survol de l'approche
orientée objet, en particulier du principe de substitution de
Liskov (ne dériver publiquement une classe d'une autre que si les
deux classes ont des
invariants mutuellement cohérents)
Retour sur les exercices proposés à la séance
S03 : une solution possible pour trouver(debut,fin,val), trouver_si(debut,fin,pred)
et pivoter_gauche(debut, fin) (je vous laisse pivoter_droite(debut, fin)
en exercice 🙂).
Ensuite, quelques exercices à réaliser pour
S06 :
- Écrivez l'algorithme trouver_consecutifs(debut,fin,n)
qui retournera
un itérateur sur le début de la première séquence de
n éléments consécutifs de même valeur dans la séquence .
- Cet algorithme retournera fin si aucune
telle séquence n'existe, ou encore si
- Écrivez l'algorithme
plus_longue_sequence(debut,fin), qui retournera une paire (voir
std::pair
et
std::make_pair()) dont le premier élément sera un itérateur
sur le début de la plus longue séquence de valeurs consécutives trouvée
dans la séquence
, et le second élément sera un itérateur sur la fin de cette
« sous-séquence ».
- Veillez à ce que la paire d'itérateurs retournée forme
une séquence à demi-ouverte.
- Si la séquence
est vide, alors retournez une paire fin,fin.
Si plusieurs séquences distinctes ont la même (plus longue) longueur,
alors retournez une paire d'itérateurs sur la première d'entre elles.
- Vous pouvez vous aider en écrivant des algorithmes auxiliaires.
- Vous avez
le droit d'utiliser
std::distance()
- Écrivez l'algorithme
plus_longue_sequence(debut,fin,pred), qui retournera une paire
(voir
std::pair
et
std::make_pair()) dont le premier élément sera un itérateur
sur le début de la plus longue séquence de valeurs consécutives
respectant le prédicat pred trouvée dans la
séquence
, et le second élément sera un itérateur sur la fin de cette
« sous-séquence ».
- Veillez à ce que la paire d'itérateurs retournée forme
une séquence à demi-ouverte.
- Si la séquence
est vide, alors retournez une paire fin,fin.
- Si plusieurs séquences distinctes ont la même (plus longue) longueur,
alors retournez une paire d'itérateurs sur la première d'entre elles.
- Vous pouvez vous aider en écrivant des algorithmes auxiliaires.
- Vous avez
le droit d'utiliser
std::distance()
Pour les exercices suivants, vous aurez besoin de déterminer si un
caractère c est un blanc ou non. Utilisez pour
ce faire la fonction std::isspace(char,std::locale)
que vous trouverez dans <locale>, en
utilisant locale{""} à titre de paramètre
pour le locale.
Notez qu'il existe aussi un std::isspace(char),
fonction du langage
C,
dans <cctype>, version
C++ de <ctype.h>, mais cette version
(plus rapide, car moins puissante) ne tient pas compte de la culture et
ne correspond pas à nos attentes pour cet exercice.
- Écrivez l'algorithme inverser_mots(s) qui
retourne une chaîne de la même taille que s,
mais dont le contenu est l'inverse de l'ordre des mots dans la chaîne
s sans toutefois modifier l'ordre et la taille des séquences de
blancs dans s.
- Par exemple :
- si s est
" j'aime mon prof ", alors la fonction retournera
" prof mon j'aime "
- si s est "yo",
alors la fonction retournera "yo"
- si s est
" yo man", alors la fonction retournera
" man yo"
- si s est "yo man",
alors la fonction retournera "man yo"
- Écrivez l'algorithme inverser_lettres(s)
qui retourne une chaîne de la même taille que s,
mais dont le contenu est tel que seul l'ordre des symboles dans chacun
des mots est inversé.
- Par exemple :
- si s est
" j'aime mon prof ", alors la fonction retournera
" emia'j nom forp "
- si s est "yo",
alors la fonction retournera "oy"
- si s est
" yo man", alors la fonction retournera
" oy nam"
- si s est "yo man",
alors la fonction retournera "oy nam"
- Écrivez l'algorithme ltrim(s) (pour
Left Trim) qui retourne une chaîne de
caractères équivalente à s, mais dont toutes les séquences de blancs
au début auront été supprimées.
- Par exemple :
- si s est
" j'aime mon prof ", alors
la fonction retournera "j'aime mon prof "
- si s est
"j'aime mon prof ", alors la
fonction retournera "j'aime mon prof "
- si s est " x ",
alors la fonction retournera "x "
- si s est " ",
alors la fonction retournera ""
- si s est "", alors la fonction
retournera ""
- Écrivez l'algorithme rtrim(s) (pour
Right Trim) qui retourne une chaîne de
caractères équivalente à s, mais dont toutes les séquences de blancs
à la fin auront été supprimées.
- Par exemple :
- si s est
" j'aime mon prof ", alors
la fonction retournera " j'aime mon prof"
- si s est
" j'aime mon prof", alors la fonction retournera
"j'aime mon prof "
- si s est " x ",
alors la fonction retournera " x"
- si s est " ",
alors la fonction retournera ""
- si s est "", alors la fonction
retournera ""
- Écrivez l'algorithme trim(s) qui retourne une chaîne de
caractères équivalente à s, mais dont toutes les séquences de blancs
au début et à la fin auront été supprimées.
- Par exemple :
- si s est
" j'aime mon prof ", alors
la fonction retournera "j'aime mon prof"
- si s est
" j'aime mon prof", alors la fonction retournera
"j'aime mon prof"
- si s est " x ",
alors la fonction retournera "x"
- si s est " ",
alors la fonction retournera ""
- si s est "", alors la fonction
retournera ""
- Écrivez l'algorithme elaguer_blancs(s) qui retourne une chaîne de
caractères équivalente à s, mais dont toutes les séquences de blancs
seront remplacées par un seul caractère d'espacement.
- Par exemple :
- si s est
" j'aime mon prof ", alors
la fonction retournera " j'aime mon prof "
- si s est
"j'aime mon prof", alors la fonction retournera "j'aime mon prof"
- si s est
" ", alors la fonction retournera
" "
- si s est
" ", alors la fonction retournera " "
- si s est "",
alors la fonction retournera ""
De ces exercices, deux seront recueillis lors de
S06 et seront notés au bulletin. Je ne sais
malheureusement (!) pas lesquels au moment d'écrire ces lignes, mais je
vous les communiquerai plus tard aujourd'hui.
|
A : 15 janvier 8 h 30-12 h
B : 21 janvier 13h-16 h 30 |
S05 |
Cours au P-116. Au menu :
- Temps pour travailler sur les exercices donnés lors de
S04, avec support
de votre chic prof
- Les exercices à remettre lors de
S06 vous seront révélés pendant la
séance... 🙂
Au début de la séance S06,
vous devrez remettre les exercices suivants :
- La fonction inverser_mots()
- La fonction
plus_longue_sequence(debut,fin,pred)
...en format imprimé.
|
A : 21 janvier 8 h-9 h 45
B : 22 janvier 8 h-9 h 45 |
S06 |
Cours au P-116
le 21
janvier, et au
P-148 le 22
janvier. Au menu :
Remise des exercices suivants :
- La fonction inverser_mots()
- La fonction
plus_longue_sequence(debut,fin,pred)
...en format imprimé, au début de la séance.
|
A : 7 février
8 h-10 h 30 B : 7 février 12 h 35-15 h 5 |
S07 |
Cours suspendus au Collège pour raisons de chute de neige un peu
ridicule
|
A : 14 février 8 h-10 h 30 B : 14 février 12 h 35-15 h 5 |
S08 |
Cours au P-148 le matin et au P-116
l'après-midi. Au menu :
- Retour sur les travaux
- Retour sur la
Liste<T>
- Généricité et
méthodes d'extension en
C#
- Introduction sommaire à LiNQ
- Examen et implémentation des mécanismes de parcours de séquence
- En
C# : énumérables et énumérateurs
- En
Java : itérables et itérateurs
- En
C++ : itérateurs
|
A : 21 février 8 h-10 h 30 B : 21 février 12 h 35-15 h 5 |
S09 |
Cours au P-148 le matin et au P-116
l'après-midi. Au menu :
Au menu, les indirections intelligentes. En particulier :
J'ai fait un exemple d'observateur
sans délégués :
using System;
using System.Collections.Generic;
interface RéactionClavier
{
void TouchePressée(char c);
}
class AfficheurTouche : RéactionClavier
{
public void TouchePressée(char c)
{
Console.WriteLine($"Touche pressée: {c}");
}
}
interface Signaleur
{
void Signaler();
}
class Surveillant : RéactionClavier
{
private Signaleur signaleur;
private char Touche { get; set; }
public Surveillant(Signaleur sign, char c)
{
if (sign == null) throw new Exception("Signaleur manquant");
signaleur = sign;
Touche = char.ToLower(c);
}
public void TouchePressée(char c)
{
if (char.ToLower(c) == Touche)
signaleur.Signaler();
}
}
class FinDeProgramme : Signaleur
{
public bool Complété { get; private set; } = false;
public void Signaler()
{
Complété = true;
}
}
class GestionnaireClavier
{
private List<RéactionClavier> Elems { get; set; } = new List<RéactionClavier>();
private GestionnaireClavier()
{
}
// note : propriété static
public static GestionnaireClavier Instance { get => singleton; };
static GestionnaireClavier singleton = null;
static GestionnaireClavier() { singleton = new GestionnaireClavier(); }
public void Abonner(RéactionClavier réac)
{
Elems.Add(réac);
}
public void Désabonner(RéactionClavier réac)
{
Elems.Remove(réac);
}
public void Exécuter()
{
var c = Console.ReadKey(true); // n'affiche pas la touche
foreach (RéactionClavier réac in Elems)
{
réac.TouchePressée(c.KeyChar);
}
}
}
class Program
{
static void Main()
{
var ges = GestionnaireClavier.Instance;
ges.Abonner(new AfficheurTouche());
var fdp = new FinDeProgramme();
ges.Abonner(new Surveillant(fdp, 'q'));
while (!fdp.Complété)
{
ges.Exécuter();
}
}
}
... que j'ai ensuite un peu modernisé :
using System;
using System.Collections.Generic;
interface RéactionClavier
{
void TouchePressée(char c);
}
class AfficheurTouche : RéactionClavier
{
public void TouchePressée(char c)
{
Console.WriteLine($"Touche pressée: {c}");
}
}
interface Signaleur
{
void Signaler();
}
class Surveillant : RéactionClavier
{
private Signaleur signaleur;
private char Touche { get; set; }
public Surveillant(Signaleur sign, char c)
{
signaleur = sign ?? throw new Exception("Signaleur manquant"); // ICI
Touche = char.ToLower(c);
}
public void TouchePressée(char c)
{
if (char.ToLower(c) == Touche)
signaleur.Signaler();
}
}
class FinDeProgramme : Signaleur
{
public bool Complété { get; private set; } = false;
public void Signaler()
{
Complété = true;
}
}
class GestionnaireClavier
{
private List<RéactionClavier> Elems { get; } = new List<RéactionClavier>();
static GestionnaireClavier()
{
singleton = new GestionnaireClavier();
}
private GestionnaireClavier()
{
}
// note : propriété static
public static GestionnaireClavier Instance { get { return singleton; } } = null; // ICI
public void Abonner(RéactionClavier réac)
{
Elems.Add(réac);
}
public void Désabonner(RéactionClavier réac)
{
Elems.Remove(réac);
}
public void Exécuter()
{
var c = Console.ReadKey(true); // n'affiche pas la touche
foreach (var réac in Elems) // ICI
{
réac.TouchePressée(c.KeyChar);
}
}
}
class Program
{
static void Main()
{
var ges = GestionnaireClavier.Instance;
ges.Abonner(new AfficheurTouche());
var fdp = new FinDeProgramme();
ges.Abonner(new Surveillant(fdp, 'q'));
while (!fdp.Complété)
{
ges.Exécuter();
}
}
}
J'ai aussi fait un exemple d'observateur
avec délégués :
using System;
using System.Collections.Generic;
delegate void RappelTouchePressée(char c); // ICI
class AfficheurTouche // plus besoin d'une interface
{
public void TouchePressée(char c)
{
Console.WriteLine($"Touche pressée: {c}");
}
}
delegate void RappelSignalement(); // ICI
class Surveillant // plus besoin d'une interface
{
private RappelSignalement signaleur;
private char Touche { get; set; }
public Surveillant(RappelSignalement sign, char c)
{
signaleur = sign ?? throw new Exception("Signaleur manquant");
Touche = char.ToLower(c);
}
public void TouchePressée(char c)
{
if (char.ToLower(c) == Touche)
signaleur(); // ICI
}
}
class FinDeProgramme // plus besoin d'interface
{
public bool Complété { get; private set; } = false;
public void Signaler()
{
Complété = true;
}
}
class GestionnaireClavier
{
private List<RappelTouchePressée> Elems { get; } = new List<RappelTouchePressée>(); // ICI
static GestionnaireClavier()
{
singleton = new GestionnaireClavier();
}
private GestionnaireClavier()
{
}
public static GestionnaireClavier Instance { get { return singleton; } } = null;
public void Abonner(RappelTouchePressée réac) // ICI
{
Elems.Add(réac);
}
public void Désabonner(RappelTouchePressée réac) // ICI
{
Elems.Remove(réac);
}
public void Exécuter()
{
var c = Console.ReadKey(true); // n'affiche pas la touche
foreach (var réac in Elems)
{
réac(c.KeyChar); // ICI
}
}
}
class Program
{
static void Main()
{
var ges = GestionnaireClavier.Instance;
ges.Abonner(new AfficheurTouche().TouchePressée); // ICI
var fdp = new FinDeProgramme();
ges.Abonner(new Surveillant(fdp, 'q').TouchePressée); // ICI
while (!fdp.Complété)
{
ges.Exécuter();
}
}
}
Voici petit exemple simple de programme affichant à la console des
identifiants légaux en
C++
(les règles sont les mêmes en
C# ou
en
Java) pris d'un fichier source nommé z.cpp :
#include <locale>
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <fstream>
using namespace std;
/*
Un identifiant dans la plupart des langages débute par une lettre ou un '_', suivi par une séquence de
zéro ou plus lettres, chiffres ou '_'. Ça donne une expression régulière comme [_a-zA-Z][_a-zA-Z0-9]+
si on l'écrit « manuellement » (car il existe des raccourcis dans bien des implémentations d'expressions
régulières pour ce genre de truc)
Le code qui suit n'est pas optimisé du tout; je l'ai écrit pour vous inspirer
*/
//
// retourne true seulement si c peut débuter un identifiant
//
bool peut_debuter_identifiant(char c, const locale &loc) {
return c == '_' || isalpha(c, loc);
}
//
// retourne true seulement si c peut poursuivre un identifiant (tout symbole
// pouvant occuper une position autre que la première dans un identifiant)
//
bool peut_poursuivre_identifiant(char c, const locale &loc) {
return c == '_' || isalnum(c, loc);
}
int main() {
//
// lire tout le fichier z.cpp dans le vecteur de char nommé texte
//
locale loc{ "" };
ifstream in{ "z.cpp" };
vector<char> texte{
istreambuf_iterator<char>{ in },
istreambuf_iterator<char>{}
};
for (auto p = begin(texte); p != end(texte); ) {
//
// trouver le début du prochain identifiant
//
while (p != end(texte) && !peut_debuter_identifiant(*p, loc))
++p;
//
// lire l'identifiant s'il y a lieu
//
if (p != end(texte)) {
string s = *p;
for (++p; p != end(texte) && peut_poursuivre_identifiant(*p, loc); ++p)
s += *p;
if (!s.empty())
cout << "Identifiant trouve : \"" << s << "\"" << endl;
}
}
}
- Dictionnaires, Hash Maps et std::map
Voici un
petit exemple d'un programme qui compte le nombre d'occurrences des « mots »
(au sens de « séquences de caractères délimitées par des blancs », donc pas
dans un sens aussi strict que celui du travail pratique proposé aujourd'hui)
et affiche chaque mot accompagné de son nombre d'occurrences (dans le
désordre), en
C++. Le code est très semblable en
C#,
et l'est à peine moins en
Java :
#include <string>
#include <map>
#include <fstream>
#include <iostream>
using namespace std;
int main() {
map<string, int> mots;
ifstream in { "z.cpp" };
for (string s ; in >> s ; mots[ s ]++)
;
for (auto & [mot, occ] : mots)
cout << "Le mot " << mot << " apparait " << occ << " fois" << endl;
}
En vue d'une remise à la séance S11 S12, je vous propose
le travail pratique suivant :
- Écrivez un programme dans le langage de votre choix (Java,
C#,
C++,
autre si vous le souhaitez... et si je vous donne mon accord au
préalable!) qui aura pour
rôle de générer une page Web contenant une version formatée d'un
fichier source
- Plus précisément, votre programme réalisera la tâche suivante :
- prendre en paramètre au démarrage (paramètres à
main() / Main()) une séquence
contenant des options et des noms de fichiers, peu importe l'ordre
- les options doivent débuter par '-'
ou
par '/' (supportez les deux formats)
- les noms de fichiers doivent être des fichiers sources dans un
langage de votre choix
- je vous suggère de prendre le même
langage que celui dans lequel vous développerez le programme, c'est
plus amusant comme ça
- le programme consommera le contenu du fichier source, le
transformera (sans modifier l'original) et écrira le résultat de la
transformation dans un fichier portant le même nom que le fichier
source, mais avec extension .html
- les transformations qui seront faites doivent dépendre des options
choisies à l'appel du programme. Si aucune option n'est choisie, alors
le programme doit générer un fichier identique au fichier source, outre
le code html qui fera du fichiers en sortie
une page Web légale
- Un exemple de lancement de votre programme (présumant qu'il se
nomme cpp2web) serait :
cpp2web -stats -couleur a.cpp b.cpp c.cpp
- ... qui générerait les fichiers a.cpp.html, b.cpp.html
et c.cpp.html de même
qu'un fichier de statistiques sur le travail accompli (le nom de ce
fichier est à votre
choix)
Pour un exécutable « exemple », vous pouvez essayer mon
cpp2web.exe mais prenez soin de placer
mots_cles.txt dans le même répertoire (le programme trouve ses mots clés dans ce fichier, alors ils doivent être au même endroit)
- L'idée est de vous amuser avec des techniques nouvelles de
programmation. Conséquemment, je vous recommande de faire le travail à
l'aide de ce qui suit (ou de l'équivalent dans votre langage de
prédilection) :
- Je m'attends à ce que vous supportiez au moins les options
suivantes :
- une option qui permettra de demander à ce que les mots clés du
langage de programmation choisi soient mis en relief (vous pouvez
utiliser du
CSS et des balises <span> pour y
arriver, c'est probablement l'option la plus simple)
- une option qui permettra de tirer des statistiques sur les données
traitées. Je vous propose de compter les mots clés, de compter le
nombre d'occurrences de chaque mot (ne comptez pas ce qui ne comprend
pas au moins un caractère alphanumérique), et de compter le nombre de
nombres (les littéraux comme 0,
3.14159 ou 85). Pas besoin de
traiter les suffixes comme U,
UL ou f (même si vous le pouvez si
vous en avez envie)
- les statistiques devront être générées dans un
fichier texte à la fin de l'exécution du programme
- les paires mot /
nombre d'occurrences devront être listées en ordre décroissant de
nombre d'occurrences, puis en
ordre lexicographique dans le cas où plusieurs mots
apparaissent aussi souvent les uns que les autres
- évidemment, seules les options demandées par l'usager devront être
mises en application (ça fait partie du plaisir)
- Vous pouvez ajouter des options si vous le souhaitez, mais
documentez ce que vous faites
- Peu importe ce que vous choisirez, n'oubliez pas que certains
symboles importants en programmation, comme <,
> ou &, jouent un rôle spécial en
html et devront être remplacés par leur métacaractère équivalent
(donc <, >
et & respectivement pour ces trois
symboles)
- Profitez-en pour vous amuser et pour expérimenter!
À la séance S10 S11 , il y
aura cours, mais je vous
laisserai aussi un peu de temps pour travailler sur ce travail pratique, et je répondrai à vos questions.
Nous pourrons discuter design, technique et autres trucs qui vous
turlupinent. Évidemment, ce cours étant en partie électronique, vous
pouvez m'écrire quand vous le souhaitez pour discuter de ces choses. Il se
peut que je répondre publiquement (donc au groupe entier) si j'estime que
votre question est d'intérêt public.
Vous aurez droit à un bonus si vous traitez correctement les cas
suivants :
- Les commentaires débutant par un // et se terminant par un saut de
ligne
- Les commentaires débutant par un /* et se terminant par un */
- Les chaînes de caractères sous la forme "abc...def"
et celles
sous la forme L"abc...def" ou
leur équivalent dans le langage que vous aurez choisi
- Les chaînes de caractères sous la forme R"(abc ... def)"
ou leur équivalent dans le langage que vous aurez choisi (en
C#, par exemple, on parle de @"abc...def")
Attention, c'est plus croustillant, car il y aura des cas comme
/*"allo" int i=3 */, "int /* " ou
/* // yeah */ qui seront des tenir-compte, alors c'est un petit
bonus par cas traité, et ne vous lancez là-dedans que si le programme de
base fonctionne
|
A : 20 mars 8 h-10 h 30 B : 20 mars 12 h 35-15 h 5 |
S10 |
Cette séance fut victime de la pandémie de Covid-19
Pour vous distraire, en atttendant, voici :
|
A : 3 avril 8 h-10 h 30 B : 3 avril 12 h 35-15 h 5 |
S11 |
Cours au P-148 le matin et au P-116
l'après-midi par Zoom (voir Colnet pour les liens). Au menu :
|
A : 17 avril 8 h-10 h 30 B : 17 avril 12 h 35-15 h 5 |
S12 |
Cours au P-148 le matin et au P-116
l'après-midi par Zoom (voir Colnet pour les liens). Au menu :
N'oubliez pas de remettre votre TP de coloriage de code source; celui
qui vous a été présenté lors de S09.
|
A : 24 avril 8 h-10 h 30 B : 24 avril 12 h 35-15 h 5 |
S13 |
Cours au P-148 le matin et au P-116
l'après-midi par Zoom (voir Colnet pour les liens). Au menu :
|
A : 1er mai 8 h-10 h 30 B :
1er mai 12 h 35-15 h 5 |
S14 |
Cours au P-148 le matin et au P-116
l'après-midi par Zoom (voir Colnet pour les liens). Au menu :
- Un peu de multiprogrammation (suite) :
- Avec
C++
- Avec
C#
- (je le ferais en
Java
aussi, mais je ne suis pas assez à jour, et
Java
change aux trois mois ces jours-ci 🙂)
- Le
faux-partage
- Ordinateurs : calculatrices ou pas?
- Questions diverses
Je n'ai pas eu le temps de corriger votre travail pratique de
coloration encore, mais pour vous divertir, voici un modèle « cheap »
d'un pipeline de transformation de contenu de fichiers (pour faire
la démonstration que, réduit à une expression relativement simple,
ce n'était pas un travail si méchant que ça) :
#include <iostream>
#include <functional>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <locale>
#include <fstream>
using namespace std;
vector<function<string(string)>> transfos() {
return {
[](string nom) -> string {
ifstream in{ nom };
return { istreambuf_iterator<char>{ in }, istreambuf_iterator<char>{} };
},
[](string s) {
static const pair<string,string> repl[] {
{ "&"s, "&"s }, { "<"s, "<"s }, { ">"s, ">"s, }
};
for (const auto &[pre, post] : repl) {
for (auto pos = s.find(pre); pos != string::npos; pos = s.find(pre, pos + post.size()))
s.replace(pos, pre.size(), post);
}
return s;
},
[](string s) {
static const string mots[] {
"if", "for", "int" // etc.
};
for (const auto &mot : mots) {
for (auto pos = s.find(mot); pos != string::npos; pos = s.find(mot, pos + mot.size()))
transform(begin(s) + pos, begin(s) + pos + mot.size(), begin(s) + pos,
[loc = locale{ "" }](char c) { return toupper(c); });
}
return s;
},
[](string s) -> string {
cout << s << endl;
return {};
}
};
}
int main() {
auto transformations = transfos();
string fichiers[]{ "z.cpp"s }; // etc.
for (auto &s : fichiers)
for (auto &tr : transformations)
s = tr(s);
}
|
22 mai 8 h-10 h 45 |
S15 |
Cours par Zoom (voir Colnet pour les liens). Au menu :
- Chic examen final plein d'amour!
|
Vous trouverez ici quelques documents, la plupart petits, qui peuvent vous
donner un petit coup de pouce occasionnel.
Les solutionnaires aux exercices auxquels vous avez été confrontés et qui
ont déjà été remis sur publiés ci-dessous