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.

Pour éviter de faire imprimer un trop grand nombre de copies, je ferai circuler une feuille en classe et je n'en ferai imprimer que pour celles et ceux parmi vous qui en font la demande.

Quand je connaîtrai les identifiants pour obtenir ces volumes à la coop, je mettrai à jour les puces ci-dessous :

Je veux ces volumes

Je ne veux pas ces volumes

Nicholas Thérien Roussel

 
   
   
   
   
   
   
   
   
   
   

 

 

Détail des séances en classe

Techniquement, vous serez deux groupes de 420KEL cet hiver, soit les groupes (a) et (b). L'horaire sera organisé de manière à alterner les séances d'avant-midi et d'après-midi d'un groupe à l'autre. Soyez donc attentives et attentifs aux variations d'horaire pour éviter de manquer une séance (dans un format intensif, c'est pas vraiment une bonne idée de se lever trop tard).

Date Séance Détails
9 janvier
S00

Groupe (a) :h à 12 h

Groupe (b) : 13 h à 16 h

Cours au P-152. Au menu :

10 janvier S01

Groupe (b) :h à 12 h

Groupe (a) : 13 h à 16 h

C'est l'anniversaire de Donald Knuth aujourd'hui!

Cours au P-152. Au menu :

  • Survol de la différence entre initialisation avec parenthèses et initialisation avec accolades
  • Rôle de la Sainte-Trinité
  • Présentation de l'idiome d'affectation sécuritaire
  • Discussions à propos de la représentation des paramètres culturels à l'aide d'instances de std::locale
  • Réflexions sur la répartition des responsabilités entre un conteneur et son contenu (dans votre code : entre ListeEntiers et Noeud)
  • Qu'est-ce qu'un invariant? (survol)
  • Qu'est-ce qu'une précondition? (survol)
  • Qu'est-ce qu'une postcondition? (survol)
  • Qu'est-ce que la complexité algorithmique? (survol)
  • Exercice de rédaction d'une version plus efficace de ListeEntiers, représentant une liste simplement chaînée d'entiers
  • Réflexion sur le problème de la méthode ajouter() de ListeEntiers, dans sa déclinaison initiale
    • discussion de solutions possibles
  • Présentation sommaire de la sémantique de mouvement
    • implémentation pour ListeEntiers
    • comparaison avec la copie
    • comparaison du rôle de la copie et du rôle du mouvement

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
11 janvier S02

Groupe (a) :h à 12 h

Groupe (b) : 13 h à 16 h

Cours au P-152. Au menu :

  • Petit truc de débogage : la classe Noisy
  • 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 S03.

16 janvier S03

Groupe (b) :h à 12 h

Groupe (a) : 13 h à 16 h

Cours au P-152. 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
  • 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
17 janvier S04

Groupe (a) :h à 12 h

Groupe (b) : 13 h à 16 h

Cours au P-152. Au menu :

  • 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)

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()
  • É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. Un caractère c est un blanc si isspace(c,locale{""}). 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"

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 au plus tard 48 heures à l'avance.

18 janvier S05

Groupe (b) :h à 12 h

Groupe (a) : 13 h à 16 h

Cours au P-152. Au menu :

Dans les notes de cours :

  • L'idiome RAII est décrit dans POO – Volume 00, Annexe 03 puis dans POO – Volume 01, pp. 206-210

Pour les intéressé(e)s, le code de la fonction distance_() « maison » va comme suit :

#include <iterator>
using namespace std;
template <class It>
   auto distance_(It debut, It fin, forward_iterator_tag) {
      typename iterator_traits<It>::difference_type n{};
      for (; debut != fin; ++debut)
         ++n;
      return n;
   }
template <class It>
   auto distance_(It debut, It fin, random_access_iterator_tag) {
      return fin - debut;
   }
template <class It>
   auto distance_(It debut, It fin) {
      return distance_(debut, fin, typename iterator_traits<It>::iterator_category{});
   }

Aussi, la classe ListeEntiers avec ébauche d'intérateur (incomplète; il manque des morceaux, mais ça donne un aperçu) allait comme suit (ajouts en caractères gras) :

#include <utility>
class ListeEntiers final {
   struct Noeud final {
      Noeud *succ{};
      int val;
      Noeud(int val) : val{ val } {
      }
      Noeud(const Noeud &n) : val{ n.val } {
      }
   };
   Noeud *tete{};
   Noeud *fin{};
   int nelems{};
public:
   //
   //
   //
   class iterator {
      Noeud * cur;
   public:
      using value_type = int;
      using reference = int&;
      using pointer = int*;
      using difference_type = std::ptrdiff_t;
      using iterator_category = std::forward_iterator_tag;
      iterator() : cur{} {
      }
      iterator(Noeud *p) : cur{} {
      }
      iterator& operator++() { // ajouter operator++(int)
         cur = cur->succ;
         return *this;
      }
      bool operator==(const iterator &autre) const {
         return cur == autre.cur; // ajouter operator!=
      }
      int& operator*() { // ajouter version const
         return cur->val;
      }
   };

   iterator begin() { return { tete }; }
   iterator end() { return { }; }
   //
   //
   //
   bool est_vide() const noexcept {
      return !tete;
   }
   int taille() const noexcept {
      return nelems;
   }
   class ListeVide { };
   void clear() {
      while (!est_vide()) extraire();
   }
   ListeEntiers() = default;
   ListeEntiers(const ListeEntiers &lst) {
      for (auto p = lst.tete; p; p = p->succ)
         ajouter(p->val);
   }
   ListeEntiers(ListeEntiers && lst)
      : tete{ lst.tete }, fin{ lst.fin }, nelems{ lst.nelems } {
      lst.tete = {};
      lst.fin = {};
      lst.nelems = {};
   }
   void swap(ListeEntiers &lst) {
      using std::swap;
      swap(tete, lst.tete);
      swap(fin, lst.fin);
      swap(nelems, lst.nelems);
   }
   ListeEntiers& operator=(const ListeEntiers &lst) {
      ListeEntiers{ lst }.swap(*this);
      return *this;
   }
   ListeEntiers& operator=(ListeEntiers && lst) {
      clear();
      tete = lst.tete;
      fin = lst.fin;
      nelems = lst.nelems;
      lst.tete = {};
      lst.fin = {};
      lst.nelems = {};
      return *this;
   }
   ~ListeEntiers() {
      clear();
   }
   void ajouter(int val) {
      auto p = new Noeud{ val };
      if (est_vide())
         tete = fin = p;
      else {
         fin->succ = p;
         fin = p;
      }
      ++nelems;
   }
   int extraire() {
      if (est_vide()) throw ListeVide{};
      auto p = tete;
      tete = tete->succ;
      int val = p->val;
      if (p == fin) fin = {};
      delete p;
      --nelems;
      return val;
   }
   // attention : version de qualité très discutable
   void inverser() {
      Noeud *nouvelleTete = {},
         *nouvelleFin = {};
      if (tete) { // cas particulier pour se charger de nouvelleFin
         auto p = new Noeud{ *tete };
         p->succ = nouvelleTete;
         nouvelleFin = nouvelleTete = p;
         p = tete;
         tete = tete->succ;
         delete p;
      }
      while (tete) {
         auto p = new Noeud{ *tete };
         p->succ = nouvelleTete;
         nouvelleTete = p;
         p = tete;
         tete = tete->succ;
         delete p;
      }
      tete = nouvelleTete;
      fin = nouvelleFin;
   }
};
9 février
16 février
S06

Groupe (a) : 9 février,h à 10 h 45

Groupe (b) : 16 février,h à 10 h 45

Cours au P-042. Au menu :

  • à déterminer
13 avril
20 avril
S07

Groupe (b) : 13 avril,h à 10 h 45

Groupe (a) : 20 avril,h à 10 h 45

Cours au P-042. Au menu :

  • à déterminer
27 avril
4 mai
S08

Groupe (a) : 27 avril,h à 10 h 45

Groupe (b) : 4 mai,h à 10 h 45

Cours au P-042. Au menu :

  • à déterminer
7 mai S09

Groupe (b) :h à 12 h

Groupe (a) : 13 h à 16 h

Cours au X-XXX. Au menu :

  • à déterminer
8 mai S10

Groupes (a) et (b) : 13 h à 16 h

Cours au X-XXX. Au menu :

  • à déterminer
  • Format particulier, dû au calendrier : tous sont invité(e)s, mais il n'y aura pas de théorie, que de la pratique avec soutien de l'enseignant
9 mai S11

Groupe (a) :h à 12 h

Groupe (b) : 13 h à 16 h

Cours au X-XXX. Au menu :

  • à déterminer
14 mai
S12

Groupe (b) :h à 12 h

Groupe (a) : 13 h à 16 h

Cours au X-XXX. Au menu :

  • à déterminer
15 mai
17 mai
S13

Groupe (a) : 15 mai, 13 h à 16 h

Groupe (b) : 17 mai, 13 h à 16 h

Cours au X-XXX. Au menu :

  • à déterminer
22 mai
S14

Groupe (b) :h à 12 h

Groupe (a) : 13 h à 16 h

Cours au X-XXX. Au menu :

  • à déterminer
23 mai S15

Groupe (a) :h à 12 h

Groupe (b) : 13 h à 16 h

Cours au X-XXX. Au menu :

  • à déterminer
24 mai S16

Groupe (b) :h à 12 h

Groupe (a) : 13 h à 16 h

Cours au X-XXX. Au menu :

  • à déterminer
25 mai S17

Groupe (a) :h à 12 h

Groupe (b) : 13 h à 16 h

Cours au X-XXX. Au menu :

  • à déterminer
28 mai S18

Groupe (b) :h à 12 h

Groupe (a) : 13 h à 16 h

Cours au X-XXX. 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.

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

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.

Résultats des travaux pratiques

Ce qui suit collige les résultats des divers travaux pratiques réalisés au cours de la session. La moyenne est exprimée en pourcentage, peu importe l'échelle d'origine pour la correction des travaux en question. Les absent(e)s sont considéré(e)s avoir eu zéro.

La valeur de exprimée dans le tableau se veut un reflet du nombre de participant(e)s; cette valeur devrait normalement être équivalente au nombre d'étudiant(e)s inscrit(e)s au cours. En pratique, la colonne indique le nombre d'individus ayant remis le travail, alors que la colonne indique le nombre d'individus qui auraient dû remettre le travail. Malheureusement, un travail non remis signifie une note de zéro (il y en a trop souvent).

Travail Consignes Remise

à venir

S??

S??

à venir

S??

S??

à venir

S??

S??

à venir

S??

S??

à venir

S??

S??

à venir

S??

S??

Activité synthèse

S00

S17

Valid XHTML 1.0 Transitional

CSS Valide !