419 lines
14 KiB
Python
419 lines
14 KiB
Python
# 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 "<stdin>", line 1, in <module>
|
||
# 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("")
|