Archive for March, 2010

Les dates

Monday, March 22nd, 2010

Lors de la pratique de son métier, un développeur sera assurément amené à manipuler des dates.

À moins d’une raison très particulière, il ne faudrait pas réinventer la roue et plutôt faire usage des classes prenant déjà en charge la problématique des dates (et de l’heure).

Qt, par exemple, met à disposition les classe QDate et QDateTime.

Mais lorsqu’on apprend la programmation, les exercices sur les dates sont des classiques. Voici ce qui est souvent demandé :

  • déterminer si une année donnée est bissextile ;
  • déterminer le jour de la semaine d’une date donnée ;
  • convertir l’année d’une date en chiffres romains (et vice-versa).

Je reviendrai certainement sur la conversion d’un chiffre arabe en chiffre romain (et vice-versa) dans un autre billet.

Déterminer si une année est bissextile

Pour le calendrier grégorien (celui que l’on utilise de nos jours), une année (le temps que met la Terre pour faire un tour complet autour du Soleil) dure 365.2425 jours.

Avec ce calendrier la méthode pour déterminer si une année donnée est bissextile est la suivante :

  • Une année est bissextile si elle se divise par 4.
  • Une année n’est pas bissextile si elle se divise par 100.
  • Toutefois, si elle se divise par 400, elle est bissextile.
bool isLeapYear(int Year) {
   return (Year % 400 == 0 || (Year % 4 == 0 && Year % 100 != 0) );
}

Mais ce calcul n’est valable qu’avec le calendrier grégorien. Or, celui-ci a été introduit le 15 octobre 1582 par le pape Grégoire XIII.

Pour toutes les années qui précèdent 1582, c’est le calendrier julien qui fait foi. Ce calendrier est basé sur une année de 365.25 jours, ce qui signifie qu’il y a une année bissextile tous les 4 ans.

Notre méthode de calcul peut donc être améliorée :

bool isLeapYear(int Year) {
   if (Year < 1582)
      return abs(Year) % 4 == 0;
   else
      return (Year % 400 == 0 || (Year % 4 == 0 && Year % 100 != 0) );
}

Déterminer le jour de la semaine

La méthode permettant de déterminer quel jour de la semaine est une date donnée est différente selon que la date fait partie du calendrier grégorien ou julien.

Le calendrier julien se termine le 4 octobre 1582. Le calendrier grégorien commence le 15 octobre 1582. Cela signifie que le jour qui suit la date du 4 octobre 1582 est le 15 octobre 1582. Les dates comprises entre le 5 et le 14 octobre 1582 n’existent pas.

prérequis : Jour (1 - 31), Mois (1-12), Année
variables : DécaleAn, MoisDécalé, An, NuméroJour : entiers
début
   DécaleAn <- (14 - Mois) / 12
   An <- Année - DécaleAn
   MoisDécalé <- Mois + 12 * DécaleAn - 2

   si (Année > 1582) ou (Année = 1582 et (Mois > 10) ou (Mois = 10 et Jour >= 15))) alors
      NuméroJour = (Jour + Année + An/4 - An/100 + An/400 + 31 * Mois / 12) modulo 7
   sinon
      si (Année < 1582) ou (Année = 1582 et (Mois < 10 ou (Mois = 10 et Jour <= 4))) alors
         NuméroJour = (5 + Jour + An + An / 4 + ( 31* Mois / 12) modulo 7
      sinon
         NuméroJour = erreur
      fin si
   fin si
fin

NuméroJour vaut 0 pour dimanche, 1 pour lundi, etc.

En C++, la fonction pourrait être la suivante :

int dayOfWeek(int Jour, int Mois, int Annee) {
   int DecaleAn = 14 - Mois / 12;
   int An = Annee - DecaleAn;
   int MoisDecale = Mois + 12 * DecaleAn - 2;
   int NumeroJour = 0;

   if ( (Annee > 1582) ||
        (Annee == 1582 && (Mois > 10) || (Mois == 10 && Jour >= 15))) )
      NumeroJour = (Jour + Annee + An/4 - An/100 + An/400 + 31 * Mois / 12) % 7;
   else if ( (Annee < 1582) ||
                (Année == 1582 && (Mois < 10 || (Mois == 10 && Jour <= 4))) )
      NumeroJour = (5 + Jour + An + An / 4 + ( 31* Mois / 12) % 7;
   else
      NumeroJour = -1; 

   return NumeroJour; // 0 : dimanche, 1 : lundi, etc. -1 : erreur
}

Pourquoi QList::count() retourne un int ?

Thursday, March 18th, 2010

Les utilisateurs de Qt4 ont peut-être remarqué que la méthode count() ou size() de QList (mais également d’autres classes comme QVector, QMap, etc) retourne un int (donc un entier signé pouvant représenter des valeurs négatives), alors qu’à première vue, un entier non-signé semblerait plus logique.

D’ailleurs, les classes équivalentes de Qt3 retournaient bien un entier non-signé (unsigned int ou uint). Pourquoi ce changement ?

Au premier abord, un entier non-signé semble plus correct qu’un entier signé pour représenter une quantité : une liste ne peut pas contenir un nombre d’éléments négatif. Toutefois à l’usage, il est fréquent de comparer un entier avec le nombre d’élément d’une liste, ce qui provoque à chaque fois un avertissement du compilateur (qui peut être supprimé en faisant un transtypage).

Mais au delà de la stricte application disant qu’une quantité ne peut pas être négative, la raison principale d’utiliser un entier signé est d’éviter des erreurs sournoises.

Par exemple, si count() retourne un entier non-signé, le code suivant fonctionne :

for (uint i = 0; i < count(); ++i)

Mais à l’opposé, le code ci-dessous ne fonctionne pas :

for (uint i = count() - 1; i >= 0; --i)

car si la liste est vide, count() retournera zéro, et au moment d’être décrémenté, il vaudra une valeur positive. C’est un problème sournoi qui ne saute pas aux yeux et qui provoque souvent des boucles quasi-infinies.

Par contre, si count() retourne un entier signé, les deux façons de faire ci-dessous fonctionnent, sans subtilité cachée :

for (int i = 0; i < count(); ++i)
for (int i = count() - 1; i >= 0; --i)

Dessin Visio dans Latex

Wednesday, March 17th, 2010

Ma situation est la suivante : j’ai des dessins vectoriels créés avec Visio que je dois inclure dans un document Latex.

Dans Latex, j’utilise la package graphics, qui permet d’inclure des fichiers PDF.

Le problème : enregistrer un dessin Visio en PDF.

Ma première tentative a été de sauvegarder le dessin en SVG, puis de le convertir en PDF avec InkScape. Malheureusement, si le dessin Visio contient des textes (de surcroît avec des polices de caractères spéciales ou avec des espaces entre lettres et entre lignes non standards), le fichier SVG produit est inutilisable.

Restaient alors deux solutions pour sauvegarder un dessin Visio en PDF : imprimer avec un distilleur PDF (par exemple PDF Creator) ou installer une extension fournie par Micro$oft qui s’intègre directement à Visio, solution que j’ai retenue.

Cette extension peut être téléchargée ici.

Une fois cette extension installée, le menu Fichier offre la fonctionnalité suivante : Publier comme PDF ou XPS...

Mais, un nouveau problème doit être résolu : le dessin que je veux inclure dans Latex n’a pas la dimension d’une page A4, il est plus petit. Or, la publication en PDF de Visio exporte toute la page. Il est possible dans les options d’exportation de n’exporter que la sélection, mais l’élément sélectionné est quand même placé sur une page A4, ce qui ne convient pas à une inclusion dans Latex.

Pour résoudre ce dernier problème, il faut utiliser une macro très pratique (DissMacros.vss) que j’ai trouvée ici (miroir local : DissMacros).

L’intégration de cette macro dans un document Visio nécessite quelques étapes simples :

  1. Télécharger la macro DissMacros.vss et l’enregistrer dans Mes Documents\Mes formes.
  2. Ajouter ces formes au document : Fichier->Formes->Mes Formes->DissMacros (activer les macros si cela vous est demandé).
  3. S’assurer que le dessin à exporter est seul sur une page.
  4. Appeler la macro ExportActivePage avec le menu Outils->Macros->DissMacros->Module 1->ExportActivePage.
  5. Cette macro redimensionne temporairement la page pour s’adapter aux dimensions du dessin et l’exporte en PDF (ainsi que dans d’autres formats).
  6. Les fichiers créés sont stockés dans un répertoire du même nom que le document Visio, situé au même emplacement que ce document.

MISE À JOUR DU 24.8.2012

Voici la marche à suivre pour Visio 2010 :

  1. Télécharger la macro DissMacros.vss et l’enregistrer dans Mes Documents\Mes formes.
  2. Ajouter ces formes au document : Fichier->Formes->Mes Formes->DissMacros (activer les macros si cela vous est demandé).
  3. Activer l’onglet Développeur : Fichier->Options->Personnaliser le ruban, cocher Développeur dans la liste à droite nommée Onglets principaux.
  4. S’assurer que le dessin à exporter est seul sur une page.
  5. Appeler la macro ExportActivePage : menu Onglet Développeur->Macros. Choisir DissMacros.vss dans la liste déroulante Macros dans.
  6. Cette macro redimensionne temporairement la page pour s’adapter aux dimensions du dessin et l’exporte en PDF (ainsi que dans d’autres formats).
  7. Les fichiers créés sont stockés dans un répertoire du même nom que le document Visio, situé au même emplacement que ce document.