# PREUD'HOMME BONTOUX Geoffrey - PeiP 12 - 2014/2015 # TP n°3 donné le 26/09/2014 - Autour du calendrier # http://www.fil.univ-lille1.fr/~wegrzyno/portail/Info/Doc/HTML/tp_conditionnelle.html # [Q] Programmez une fonction nommée est_divisible_par def est_divisible_par(a, b): """ Indique si a est divisible par b. CU : a entier, b entier Exemple : >>> est_divisible_par(2016, 4) True """ # Si a est divisible par b, alors le reste de la division Euclidienne doit # être nul return a % b == 0 # [Tests] # >>> est_divisible_par(1337, 7) # True # >>> est_divisible_par(997, 3) # False # [Q] Programmez une fonction nommée est_bissextile def est_bissextile(annee): """ Indique si l'année a donnée est bissextile. CU : annee entier > 1582 Exemple : >>> est_bissextile(2016) True """ # Transcription des données du sujet du TP en langage Python # L'ordre des opérations [(A⋀B)⋁C] ou [A⋀(B⋁C)] est indifférent ici return est_divisible_par(annee, 4) and \ (not est_divisible_par(annee, 100) or est_divisible_par(annee, 400)) # [Tests] # >>> est_bissextile(2014) # False # >>> est_bissextile(2016) # True # >>> est_bissextile(2100) # False # >>> est_bissextile(2000) # True # [Q] Programmez une fonction nommée nbre_jours def nbre_jours(m, a): """ Renvoie le nombre de jours contenus dans un mois m d’une année a donné. CU : m entier, a entier, 1 ≥ m ≥ 12, a > 1582 Exemple : >>> nbre_jours(5, 1968) 31 """ # [Q] Complétez votre fonction nbre_jours avec assert() assert(type(m) is int and m in range(1, 13)), \ "Le mois doit être un entier compris entre 1 et 12 inclus." assert(type(a) is int and a > 1582), \ "L'année doit être un entier supérieur à 1582." # [/Q] if m in [4, 6, 9, 11]: # Si le mois fait partie de ceux à 30 jours return 30 elif m == 2: # Sinon, si le mois est février if est_bissextile(a): # Si l'année est bissextile return 29 else: return 28 else: # Si les conditions ci-dessus ne sont pas vérifiées, c'est forcément # un mois de 31 jours return 31 # [Q] Testez votre fonction pour tous les mois de l’année 2014 et le mois # de février 2016 test = "Test nbre_jours() : " # On crée une variable pour imprimer # une seule ligne for test_mois in range(1, 13): # On teste pour tous les mois de 2014 test += str(nbre_jours(test_mois, 2014)) + ", " test += str(nbre_jours(2, 2016)) print(test) # Enfin, on affiche # Test nbre_jours() : 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29 # [Q] Que donne votre fonction avec un mois m=-1 ? Est-ce normal ? # >>> nbre_jours(-1, 2014) # 31 # On constate que la fonction retourne une valeur qui aurait pu être associée # à certains mois valides. Le fait que la fonction retourne une valeur et ne # déclenche pas une erreur d'elle-même est normal, car à aucun moment la # validité des données (telles que définies dans les CU) passées en paramètres # n'est vérifiée. Il aurait d'ailleurs été tout à fait possible que la fonction # déclenche une erreur à cause d'une sous-fonction/procédure ne supportant pas # qu'on lui passe certaines données interdites. # La valeur retournée est 31, car la fonction est constituée d'une série # d'instructions conditionelles. Dès lors qu'aucune des conditions n'est # vérifiée, (m=-1 n'appartient pas aux mois à 30 jours ni au mois de février), # il sera executé l'instruction "par défaut" du else, c'est à dire retourner la # valeur 31. # [Q] Quelle condition d’utilisation faut-il envisager normalement pour # cette fonction ? # Il faut vérifier les valeurs passées en argument, afin de produire une erreur # si ces dernières ne sont conformes au CU. # De cette manière, le script s'arrêtera, et affichera un texte d'erreur # décrivant le problème. L'avantage de faire cela au lieu de ne rien # vérifier, est que le problème sera tout de suite montré au développeur # utilisant la fonction, de cette manière cette dernière ne continuera pas en # renvoiant une valeur fausse, qui faussera la partie du programme se basant # sur celle-ci. Cette pratique permet donc d'économiser du temps de débogage. # [Q] Quelle condition d’utilisation faut-il envisager normalement pour # cette fonction ? # Il faut vérifier que le mois et l'année soient entier, que le mois soit # valide, c'est à dire compris entre 1 et 12 inclus, et que la date # soit supérieure au 15 octobre 1582 (nous arrondirerons à l'année, bien qu'il # soit possible de vérifier si la date fournie est supérieure à la date de # début du calendrier grégorien au jours près, il n'est pas nécessaire de # rajouter de tels calculs en supplément pour un cas de figure qui ne sera # probablement jamais rencontré dans le cadre de ce TP). # [Q] Tapez les instructions. # >>> a = 1 # >>> assert(a != 0), "a doit être différent de 0" # Aucun message ne s'est affiché, ni aucune erreur ne s'est produite. # a est bien différent de 0 comme le veut la chaîne de caractère, et cette # condition est vérifiée à l'interieur de l'instruciton assert() # >>> a = 0 # >>> assert (a != 0), "a doit être différent de 0" # Traceback (most recent call last): # File "", line 1, in # AssertionError: a doit être différent de 0 # Un message d'erreur ayant pour message "AssertionError: a doit être différent # de 0" pour est affiché. a n'est pas différent de 0, la condition dans # l'instruction assert() n'est pas vérifiée. # Nous pouvons en déduire que assert() vérifie que le test qui lui est # donné est vrai (c'est donc un booléen), et sinon produit une erreur # avec pour message le texte qui lui est apposé. Cette instruction pourra être # utilisée pour vérifier les CU d'une fonction/procédure. # [Q] Complétez votre fonction nbre_jours avec cette instruction # Voir nbre_jours(), le code ajouté pour cette question a été encadré. # Une assertion pour l'année a été ajoutée pour garder une cohérence # avec les CU. # [Q] Trouvez différents cas de non validité d’un triplet d’entiers pour # représenter une date. # Une date peut être invalide si son numéro de jour est inférieure strictement # à 1 ou supérieur strictement au nombre de jours que peut comporter ce mois, # ou bien si son numéro de mois est inférieur strictement à 1 ou supérieur # strictement à 12. On considèrera comme invalide une date inférieure à 1582, # car il est préférable de renvoyer une erreur indiquant que le programme ne # gère pas les dates hors du calendrier grégorien plutôt que de renvoyer une # valeur faussée, ce qui pourrait mener à d'éventuelles complications. # Exemples : (-14, 7, 1789), (29, 2, 1997), (31, 6, 2014), (28, 120, 1969) # [Q] Programmez une fonction nommée est_date_valide def est_date_valide(j, m, a): """ Indique si la date donnée appartient au calendrier grégorien. CU : -aucune- Exemple : >>> est_date_valide(25, 4, 1983) True """ # Il n'est pas nécessaire de fournir de CU donc ne pas écrire d'instruction # assert() car c'est le but même de la fonction. # On vérifie les types en premier pour éviter de faire fonctionner les # opérateurs sur d'éventuels types ne les supportant pas. # La vérification du jour, donc l'appel de nbre_jours() est fait en dernier # car cette dernière fonction nécessite que le mois et l'année soient # valides. return type(j) is int and type(m) is int and type(a) is int and a > 1582 \ and m in range(1, 13) and j in range(1, nbre_jours(m, a)+1) # [Tests] # >>> est_date_valide(25, 4, 1983) # True # >>> est_date_valide(-14, 7, 1789) # False # >>> est_date_valide(29, 2, 1997) # False # >>> est_date_valide(31, 6, 2014) # False # >>> est_date_valide(28, 120, 1969) # False # [Q] Le 15/09/2014 est un lundi. Donc, pour cette date, le numéro à # calculer doit être 1. Vérifiez ce point. # Un rapide et simple calcul à l'aide du prompt de Python nous permet de le # vérifier. On est alors sûr de la syntaxe à utiliser pour num_jour(). # >>> (2014%100//4 + 2014//100//4 + 2014%100 + 2 + 15 + 2 + 5 * 2014//100) % 7 # 1 # [Q] Programmez une fonction nommée corrige_mois def corrige_mois(m, a): """ Renvoie la valeur corrigée du mois selon l’algorithme de Delambre. CU : m entier, a entier, 1 ≤ m ≤ 12, a > 1582 Exemple : >>> corrige_mois(2, 2016) 6 """ assert(type(a) is int and a > 1582), \ "L'année doit être un entier supérieur à 1582" assert(type(m) is int and m in range(1, 13)), \ "Le mois doit être un entier compris entre 1 et 12 inclus" # On crée un tableau de valeurs pour un accès facile aux données liste_corrections_mois = [4, 0, 0, 3, 5, 1, 3, 6, 2, 4, 0, 2] # Si l'année est bissextile, on y modifie uniquement les valeurs nécessaires if est_bissextile(a): liste_corrections_mois[0] = 3 liste_corrections_mois[1] = 6 # On prend dans le tableau la valeur qu'il faut # On enlève 1 à l'index du tableau, car les mois comment à 1 # alors que le tableau commence lui à 0 return liste_corrections_mois[m-1] # [Tests] # >>> corrige_mois(2, 2016) # 6 # >>> corrige_mois(9, 2014) # 2 # [Q] Programmez une fonction nommée num_jour, paramétrée par trois # entiers représentant une date supposée valide, qui renvoie le numéro du jour # dans la semaine correspondant à cette date. def num_jour(j, m, a): """ Renvoie le numéro du jour dans la semaine correspondant à la date donnée. CU : Date valide (selon est_date_valide()) Exemple : >>> num_jour(9, 11, 2004) 2 """ # On peut utiliser le prélicat est_date_valide() précédemment écrite # ce qui nous permet de nous libérer d'instructions assert() et de CU assert(est_date_valide(j, m, a)), "La date n'est pas valide" # On stocke les valeurs utilisées plusieurs fois dans des variables pour # éviter de refaire deux fois le même calcul ab = a//100 cd = a%100 return (cd//4 + ab//4 + cd + corrige_mois(m, a) + j + 2 + 5 * ab) % 7 # [Tests] # >>> num_jour(9, 11, 2004) # 2 # >>> num_jour(24, 10, 1929) # 4 # [Q] Programmez une fonction nommée nom_jour def nom_jour(j, m, a): """ Renvoie le nom du jour dans la semaine correspondant à la date donnée. CU : Date valide (selon est_date_valide()) Exemple : >>> nom_jour(24, 10, 1929) "Jeudi" """ assert(est_date_valide(j, m, a)), "La date n'est pas valide" num = num_jour(j, m, a) # Au cas où la fonction num_jour() viendrait à dysfonctionner assert(num in range(7)), "Jour de la semaine inconnu" # On stocke les jours dans un tableau pour un accès facilité liste_jours_semaine = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", \ "Vendredi", "Samedi"] return liste_jours_semaine[num] # [Tests] # >>> nom_jour(24, 10, 1929) # 'Jeudi' # >>> nom_jour(9, 11, 2004) # 'Mardi' # [Q] Programmez une procédure imprimer_mois def imprimer_mois(m, a): """ Imprime le calendrier du mois de l'année donné. CU : m entier, a entier, 1 ≤ m ≤ 12, a > 1582 Exemple : >>> imprimer_mois(9, 2014) Septembre 2014 di lu ma me je ve sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 """ assert(type(a) is int and a > 1582), \ "L'année doit être un entier supérieur à 1582" assert(type(m) is int and m in range(1, 13)), \ "Le mois doit être un entier compris entre 1 et 12 inclus" # Affichage mois et année # On stocke les mois dans un tableau pour un accès facilité liste_mois = ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", \ "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"] # On enlève 1 à l'index du tableau, car les mois comment à 1 # alors que le tableau commence lui à 0 # On stocke dans une variable le titre brut (sans alignement / espacement) titre = liste_mois[m-1] + " " + str(a) # On affiche le titre centré en répètant sur la droite un certain nombre # de caractères d'espacements. Ce dernier est calculé en enlevant la # longueur du titre àl a longueur maximale de la ligne et en divisant le # tout par deux print(" " * ((20-len(titre))//2) + titre) # Affichage nom des jours de la semaine print("di lu ma me je ve sa") # Affichage des jours jour = 1 # Le jour à écrire (s'incrémente) jour_semaine_1er = num_jour(jour, m, a) # Premier jour de la semaine nbre_jours_mois = nbre_jours(m, a) # Nombre de jours du mois # Boucle sur les lignes. # Ne s'arrête pas tant que le jour final a été atteint while jour <= nbre_jours_mois: # La variable ligne stockera la ligne avant de l'imprimer ligne = "" # On l'initialise / l'efface # Boucle sur les jours de la semaine. for jour_semaine in range(7): # Si on en est au premier jour (donc sur la première ligne) mais que # le premier jour n'a pas été dépassé, on ajoute du "vide" if jour == 1 and jour_semaine < jour_semaine_1er: ligne += " " # Sinon, et si on a pas dépassé le nombre de jours elif jour <= nbre_jours_mois: # On ajoute un caractère d'espacement si le nombre ne possède # qu'un chiffre pour l'aligner à droite if jour < 10: ligne += " " # On ajoute le nombre du jour ligne += str(jour) # On incrémente jour pour passer au prochain jour jour += 1 # Si on est entre deux jours, on ajoute un caractère d'espacement if jour_semaine < 6 and jour <= nbre_jours_mois: ligne += " " print(ligne) # On affiche la ligne # [Tests] # >>> imprimer_mois(9, 2014) # Septembre 2014 # di lu ma me je ve sa # 1 2 3 4 5 6 # 7 8 9 10 11 12 13 # 14 15 16 17 18 19 20 # 21 22 23 24 25 26 27 # 28 29 30 # >>> imprimer_mois(5, 1968) # Mai 1968 # di lu ma me je ve sa # 1 2 3 4 # 5 6 7 8 9 10 11 # 12 13 14 15 16 17 18 # 19 20 21 22 23 24 25 # 26 27 28 29 30 31 # [Q] Écrivez une instruction utilisant la procédure imprimer_mois pour # imprimer tous les mois de cette année # Boucle sur les mois for mois in range(1, 13): imprimer_mois(mois, 2014) # Si on est entre deux mois, on ajoute une ligne d'espacement if mois < 12: print("")