Donc 54.654 doit donner 8, ce n’est pas ce que vous disiez plus haut, maxime et toi.
Ma première proposition :
Je me suis basé sur (54.654 => Note de 8.5) pour savoir quel seuil choisir.
Quand un temps est compris entre deux seuils de notation, la question est de savoir lequel prendre. Celui du haut ou du bas ?
En me basant sur (54.654 => Note de 8.5) j’ai cru que tu voulais prendre le seuil supérieur. Alors qu’en fait c’est (54.654 => Note de 8), donc le seuil inférieur qu’il faut prendre.
C’est pourquoi il y a un décalage d’un seuil de notation entre mon code et ce que tu désires vraiment.
Encore une preuve qu’il faut faire attention en donnant les spécifications d’une application, ce que l’on appelle le cahier de charge !
C’est facile à corriger. Puisque mon système fonctionne en calculant les écarts temps élève/seuils notation et en éliminant les valeurs négatives, il suffit de modifier la formule de calcul.
Calcul à partir du seuil supérieur :
let ecarts = tableau.map {valeur - $0}
54.654 - 54.200 = 0.454 (distance seuil supérieur)
54.654 - 54.800 = -0.146 (distance seuil inférieur)
Pour ne garder que le seuil supérieur, j’élimine l’écart négatif.
Calcul à partir du seuil inférieur
Pour privilégier le seuil inférieur, il faut juste inverser la formule de calcul, ce qui inverse aussi le signe du résultat.
let ecarts = tableau.map {$0 - valeur}
54.200 - 54.654 = -0.454 (distance seuil supérieur)
54.800 - 54.654 = 0.146 (distance seuil inférieur)
Cette fois-ci en éliminant la valeur négative, je récupère le seuil inférieur et le tour est joué …
Sauf que … après avoir inversé le sens de la formule j’ai exécuté mon programme de tests, qui a alors … planté l’application !
Le problème vient des valeurs de tests que j’ai tapé à la vas-vite. Mon barème est très incomplet. J’ai juste pris quelques valeurs pour tester.
let tempsGarcon = [0.0, 52.400, 53.000, 53.600, 54.200, 54.800, 55.400]
let tempsFille = [0.0, 62.800, 63.400, 64.000, 64.600, 65.200, 65.900]
let listeNotes = [10.0, 10.0, 9.5, 9, 8.5, 8, 7.5]
Le dernier échantillon de tests (55.987) est en dessous du barème :
let echantillonsTests = [52.300, 53.254, 54.654, 54.932, 55.987]
Quand l’application cherche le seuil inférieur à 55.987, elle ne le trouve pas, ce qui plante le programme. Si j’avais utilisé une version du code basée sur les optionals, il n’y aurais pas eu de plantage brutale.
Comment éviter ça ? Une suggestion : éviter de mettre un temps hors barème dans la liste des calculs à faire. C’est facile a faire dans un code de démonstration, mais plus compliqué dans la vie réelle. Une simple erreur de saisie peut entrer une valeur erronée et provoquant un plantage plus tard.
Tu peux utiliser les opérateurs de tableau .min() et .max() pour savoir si une valeur de temps est comprise dans le barème.
Exemple de fonction vérifiant si un temps donné est bien compris dans une liste de valeurs :
func verificationTempsValide(temps:Double, tableau:[Double]) -> Bool {
// Vérification si le tableau a des valeurs min et max valides
// pour éviter le gag du tableau vide par exemple (cas vécu !)
guard let tempsMin = tableau.min(),
let tempsMax = tableau.max()
else { return false }
// Vérification si le temps est bien compris
// dans les valeurs min et max du tableau
if temps < tempsMin { return false }
if temps > tempsMax { return false }
// réponse positive
return true
}
Comment vérifier si mes échantillons de tests sont bien valables ?
let echantillonsTests = [52.300, 53.254, 54.654, 54.932, 55.987]
for temps in echantillonsTests {
let sexe = TypeEleve.garcon
if verificationTempsValide(temps: temps, tableau: tempsGarcon) {
print ("Le temps ", temps, " est valide")
} else {
print ("Le temps ", temps, " n'est PAS VALIDE")
}
// let note = calculNote(temps: temps, typeEleve: sexe)
// print ("Temps : ", temps, " Note : ", note)
}
Affichage :
Le temps 53.254 est valide
Le temps 54.654 est valide
Le temps 54.932 est valide
Le temps 55.987 n’est PAS VALIDE
Autre suggestion : utiliser les optionals un peu partout, pour éviter le plantage en cas de mauvaises valeurs, et remonter l’information vers l’interface utilisateur.
Une remarque technique sur la perte de temps occasionnée par l’utilisation des opérateurs globaux de tableau par rapport à une routine optimisée utilisant une boucle. On s’en fiche ! La perte de temps est infime, de l’ordre de quelques ms, voir moins. Il faut toujours privilégier la simplicité du code et la lisibilité du code à une optimisation prématurée.
Je ne dirais pas la même chose s’il s’agissait de calculer les notes de 10.000 ou 100.000 élèves en une seule fois. Là les petites pertes de temps se cumulent, pouvant créer un ralentissement visible de d’application. Tout dépend des contraintes de l’application.
Et encore le seul véritable moyen de mesurer l’intérêt d’une optimisation c’est de faire des tests. Il y a des outils dans Xcode permettant de mesurer le temps réel pris par une fonction pour traiter un calcul.
La bonne démarche, surtout pour un novice, c’est :
-
je code des choses simples et lisibles sans chercher la prouesse technique.
-
je teste
-
je note ce qui ralentit l’application
-
je cherche à optimiser juste ce qui est nécessaire
Dernière remarque, ta méthode d’ajouter +1 à mon code est une mauvaise idée. C’est une rustine magique qui aurait pu causer de nouveaux problèmes, ce qu’on appelle des effets de bords.
Dans ce cas précis, cela corrige parfaitement le problème, à 100%. Mais dans une autre situation (algorithme erroné + rustine magique) tu pourrais créer une solution fonctionnant dans 99% des cas, et plantant l’application 1 fois sur 100 ou 1 fois sur 1000. Pas facile à détecter, surtout si cela n’apparaît que de temps en temps chez l’utilisateur final.
J’ai corrigé le problème en cherchant l’erreur dans l’algorithme et en modifiant la formule de calcul, sans ajouter une valeur magique. C’est la démarche à aborder : toujours essayer de comprendre pourquoi ça ne marche pas, plutôt que d’appliquer un correctif magique sans trop comprendre pourquoi cela semble marcher comme ça !