420KEL – Intégration de techniques nouvelles en informatique de gestion

Quelques raccourcis :

Ceci est un petit site de support pour le cours 420-KEL-LG – Intégration de techniques nouvelles en informatique de gestion. Il se construira au fil des semaines à partir de ce que nous aurons effectivement fait en classe (j'ai mon plan, mais on restera agiles de manière à être le plus adaptés possible à vos besoins et à votre réalité).

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 »).

J'aimerais vous proposer quatre volumes de programmation orientée objet (entre autres) plutôt touffus, qui pourront vous être utiles dans le cours comme dans votre carrière, mais étant donné que vous êtes déjà en stage, je comprendrais si vous préfériez ne pas vous les procurer. Ces volumes présentent des exemples et des concepts en C++ (principalement), mais aussi en C#, en Java et en VB.NET.

Exceptionnellement, j'ai obtenu la permission de vous les rendre disponibles en format PDF verrouillé contre impression. Elles ne sont pas aussi à jour que d'habitude (c'est pour ça que j'ai eu la permission de vous les offrir!), mais elles sont pas mal tout de même. Les liens sont ci-dessous, mais ils ne seront pas là indéfiniment (je vais les enlever autour du début juillet). Ne les faites pas circuler s.v.p. : c'est un cadeau pour vous seules / seuls.

Détail des séances en classe

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.

Pour quelques exemples de programmes de tests (incomplets, mais si ça peut vous aider à démarrer), voir 420KEL--Exercices-S04-tests-possibles.html

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 &lt;, &gt; et &amp; 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, "&amp;"s }, { "<"s, "&lt;"s }, { ">"s, "&gt;"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!

Documents sous forme électronique

Cliquez sur cette cible pour le plan de cours, sous forme électronique

Exercices de révision  : cliquez sur cette cible pour quelques exercices de révision (gestion)

Exercices de révision  : cliquez sur cette cible pour quelques exercices de révision (industrielle)

Petits coups de pouces

Vous trouverez ici quelques documents, la plupart petits, qui peuvent vous donner un petit coup de pouce occasionnel.

Comment accéder à du code .NET à partir d'un client C++ natif

Introduction aux templates

Introduction aux foncteurs

Introduction aux conteneurs et aux itérateurs

Programmation générique appliquée

Dans la plupart des cas, vous trouverez de l'aide précieuse dans les sections Divers – C++, Au secours Pat et Trucs scouts du site.

Solutionnaires

Les solutionnaires aux exercices auxquels vous avez été confrontés et qui ont déjà été remis sur publiés ci-dessous

Cliquez sur cette cible si vous souhaitez un solutionnaire pour les exercices de révision (gestion)

Cliquez sur cette cible si vous souhaitez un solutionnaire pour les exercices de révision (industrielle)

Cliquez sur cette cible si vous souhaitez un solutionnaire pour les exercices formatifs sur la la généricité et les expressions λ avec C#

Cliquez sur cette cible si vous souhaitez un solutionnaire pour les exercices d'écriture d'algorithmes génériques en C++ proposés à la séance S04

Cliquez sur cette cible si vous souhaitez des exemples de programmes de tests pour les exercices d'écriture d'algorithmes génériques en C++ proposés à la séance S04

Correction

Je corrige les programmes en appliquant des codes de correction. Vous trouverez ici la liste des codes les plus fréquents.

Ma stratégie de correction en tant que telle (pour le code, à tout le moins) est résumée ici.


Valid XHTML 1.0 Transitional

CSS Valide !