Vidéo 41 : Fonction comme boîte noire¶
Formule de Keith et Craver¶
La formule de Keith et Craver permet de déterminer le jour de la semaine (ie lundi, mardi, etc) correspondant à une date donnée (par exemple, le 14 juillet 1789 qui était un mardi).
Ci-dessous, en voici une implémentation sous forme de fonction Python. La fonction kc
retourne sous forme d’entier (\(\texttt{1 = lundi, 2 = mardi, ..., 7 = dimanche}\)) le jour de la semaine correspondant à une date passée en paramètre comme suit : j
(jour), m
(mois) et a
(année).
1 2 3 | def kc(j, m, a):
z = a - (m<3)
return (j + 23*m//9 + 3 -2*(m>=3) + a + z//4 - z//100 + z//400)%7 +1
|
Voici un exemple d’utilisation :
1 2 3 4 5 6 | def kc(j, m, a):
z = a - (m<3)
return (j + 23*m//9 + 3 -2*(m>=3) + a + z//4 - z//100 + z//400)%7 +1
# Test du jour de la semaine du 14 juillet 2018
print(kc(14, 7, 2018))
|
7 | 6
|
- Lignes 6 et 7 : le 14 juillet 2018 est un samedi (6e jour de la semaine).
Cette fonction doit être utilisée telle quelle, sans chercher à comprendre comment elle fonctionne. Vous devez réécrire (copier-coller) une fois et une seule cette fonction pour pouvoir l’utiliser par la suite mais il est inapproprié de copier/coller le corps de la fonction lignes 2 et 3 dans votre propre code. Il est seulement attendu d”utiliser la fonction kc
.
Vérifier la validité des dates suivantes en calculant leur code entre 1 et 7 :
- dimanche 13 janvier 2019
- mardi 14 juillet 1789
- dimanche 10 mai 1981
- jeudi 16 juillet 1998
- mardi 19 janvier 2038 (le bug de l’an 2038)
En utilisant un appel à la fonction
kc
, écrire une fonctionest_vendredi13(m,a)
qui renvoie True si le 13 du mois m et de l’année a est un vendredi (et False sinon). Combien y-a-t-il de vendredis 13 dans l’année 2018 ?Ecrire et tester une fonction
jour_semaine
qui accepte en argument une listedate
, supposée de la forme[jour, mois, année]
et qui retourne le jour de la semaine correspondant en toutes lettres (ex : lundi, mardi, .., dimanche).Par exemple,
jour_semaine([14, 7, 1789])
vaut la chaînemardi
.On pourra utiliser une liste
JOURS_SEMAINE
formée des noms des jours de la semaine.
Solution
- Il suffit d’appeler la fonction
kc
avec le jour du mois, le numéro de mois et l’année et de vérifier par lecture directe que le jour de la semaine correspond. Pour la première date, cela donne :
1 2 3 4 5 | def kc(j, m, a):
z = a - (m<3)
return (j + 23*m//9 + 3 -2*(m>=3) + a + z//4 - z//100 + z//400)%7 +1
print(kc(13,1, 2017), "->", "vendredi ?")
|
6 | 5 -> vendredi ?
|
ce qui correspond au résultat annoncé.
Pour les autres dates, il est plus simple d’utiliser une boucle qui parcourt la liste des dates :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def kc(j, m, a):
z = a - (m<3)
return (j + 23*m//9 + 3 -2*(m>=3) + a + z//4 - z//100 + z//400)%7 +1
dates=[
[13,1, 2017, 5],
[14, 7, 1789, 2],
[10,5, 1981, 7],
[16,7, 1998, 4],
[1,5, 2018, 2],
[19,1, 2038, 2]
]
for i in range(len(dates)):
d=dates[i]
jourSemaine = kc(d[0], d[1], d[2])
print(jourSemaine == d[3])
|
18 19 20 21 22 23 | True
True
True
True
True
True
|
- Lignes 5-11 : on place les 6 dates à tester dans une liste
dates
. Chaque date est codée par une liste(j, m, a, js)
oùjs
est le code du jour de la semaine qu’il faut vérifier. - Lignes 13-16 : On parcourt la liste
dates
. Chaque dated
est testée. Pour cela on extrait avec des indices le jour, le mois et l’année et on demande à la fonctionkc
de calculer le jour de la semaine (ligne 15). On compare ensuite la réponse donnée parkc
avec le jour donné dans l’énoncé. - Lignes 18-22 : tous les tests sont positifs.
- Pour tester si le mois
m
de l’annéea
comporte un vendredi 13, on demande à la fonctionkc
de nous calculer le jour de la semaine du 13 du moism
de l’annéea
. Si le réponse est 5, c’est que c’est un vendredi. D’où le code suivant :
1 2 3 4 5 6 7 8 9 10 11 12 | def kc(j, m, a):
z = a - (m<3)
return (j + 23*m//9 + 3 -2*(m>=3) + a + z//4 - z//100 + z//400)%7 +1
def estVendredi13(m, a):
jourSemaine=kc(13, m, a)
if jourSemaine == 5:
return True
else:
return False
estVendredi13(1, 2017)
|
13 | True
|
Il est largement possible (et préférable) de simplifier le code :
1 2 3 4 | def estVendredi13(m, a):
return kc(13, m, a) == 5
estVendredi13(1, 2017)
|
Pour déterminer le nombre de vendredis 13 de l’an 2017, il suffit de tester si le 13 janvier 2017 est un vendredi, si le 13 février 2017 est un vendredi et ainsi de suite jusqu’à décembre. Il suffit donc de tester tous les mois m
de 1 à 12 avec un boucle for
. D’où le code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def kc(j, m, a):
z = a - (m<3)
return (j + 23*m//9 + 3 -2*(m>=3) + a + z//4 - z//100 + z//400)%7 +1
def estVendredi13(m, a):
if kc(13, m, a) == 5:
return True
else:
return False
nbVendredis13=0
for m in range(1, 13):
if estVendredi13(m, 2017):
nbVendredis13 = nbVendredis13 + 1
print(nbVendredis13)
|
18 | 2
|
- Ligne 11 : le compteur de vendredis 13.
- Lignes 14-15 : si le 13 du mois
m
de 2017 est un vendredi, on incrémente le compteur. - Ligne 18 : il y a 2 vendredis 13 en 2017.
- Il suffit d’appeler la fonction
kc
et, en fonction du code qu’elle retourne (1, 2, 3, etc), la fonctionjour_semaine
renvoie le jour de la semaine adéquat, en toute lettres, sous forme de chaîne de caractères. D’où le code suivant :
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 | def kc(j, m, a):
z = a - (m<3)
return (j + 23*m//9 + 3 -2*(m>=3) + a + z//4 - z//100 + z//400)%7 +1
def jour_semaine(date):
j=date[0]
m=date[1]
a=date[2]
code=kc(j,m,a)
if code==1:
return "lundi"
if code==2:
return "mardi"
if code==3:
return "mercredi"
if code==4:
return "jeudi"
if code==5:
return "vendredi"
if code==6:
return "samedi"
if code==7:
return "dimanche"
for date in [[13,1,2017],[14,7,1789], [10,5,1981], [16,7,1998],
[1,5,2017], [19,1,2038]]:
print(date, "->", jour_semaine(date))
|
29 30 31 32 33 34 | [13, 1, 2017] -> vendredi
[14, 7, 1789] -> mardi
[10, 5, 1981] -> dimanche
[16, 7, 1998] -> jeudi
[1, 5, 2017] -> lundi
[19, 1, 2038] -> mardi
|
- Lignes 12-23 : la suite de
if
est légitime dans la mesure où chaque corps d’instructionif
se termine par une instructionreturn
et par suite, il n’y a aucun test inutile (la fonction s’interrompt dès qu’elle a trouvé la bonne chaîne).
Alternative¶
Il existe cependant une façon plus idiomatique de procéder. En effet, les codes de jours sont consécutifs de 1 à 7, donc si on dispose d’une liste L
telle qu’à l’indice i
figure la chaîne de caractères représentant le \(\mathtt{i^\text{e}}\) jour de la semaine, il suffit d’appeler L
avec l’indice i
. Comme le code 0 ne correspond à aucun jour de la semaine, on mettra n’importe quoi à l’indice 0 de la liste L
. D’où la liste suivante qu’on va appeler plutôt JOURS_SEMAINES
au lieu de L
:
1 2 | JOURS_SEMAINES = ["toto","lundi", "mardi", "mercredi",
"jeudi", "vendredi", "samedi", "dimanche"]
|
Ainsi, par exemple, pour obtenir le 3e jour de la semaine :
1 2 3 4 | JOURS_SEMAINES = ["toto","lundi", "mardi", "mercredi",
"jeudi", "vendredi", "samedi", "dimanche"]
print(JOURS_SEMAINES[3])
|
5 | mercredi
|
On en déduit le code plus simple suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def kc(j, m, a):
z = a - (m<3)
return (j + 23*m//9 + 3 -2*(m>=3) + a + z//4 - z//100 + z//400)%7 +1
def jour_semaine(date):
j=date[0]
m=date[1]
a=date[2]
code=kc(j,m,a)
return JOURS_SEMAINES[code]
JOURS_SEMAINES = [ "toto", "lundi", "mardi", "mercredi",
"jeudi", "vendredi", "samedi", "dimanche"]
for date in [[13,1,2017],[14,7,1789], [10,5,1981], [16,7,1998],
[1,5,2017], [19,1,2038]]:
print(date, "->", jour_semaine(date))
|
19 20 21 22 23 24 | [13, 1, 2017] -> vendredi
[14, 7, 1789] -> mardi
[10, 5, 1981] -> dimanche
[16, 7, 1998] -> jeudi
[1, 5, 2017] -> lundi
[19, 1, 2038] -> mardi
|