#+title: 7d.nz
#+author: Phil. Estival
* • [2019-03-04 lun.] Bilan des résultats des élèves :code:fr:
#+results:
en C, Python, Go et Awk
Traduction en anglais
file:./img/monk.png
** Présentation
En fin d’année scolaire, le professeur d’une
classe souhaite faire le bilan des résultats de
ses élèves en pondérant les notes obtenues dans
trois matières : français (poids 10),
mathématiques (poids 8) et histoire-géographie
(poids 6). La note maximale dans chaque matière
étant 10, la note pondérée maximale est 240.
Comme résultat, il veut classer ses élèves en
trois groupes : ceux qui ont au moins 180, ceux qui
ont au moins 120 et les autres (qui ont donc moins
de 120) et imprimer, pour chaque groupe, le nom de
l’élève, les notes obtenues dans les matières
concernées et la note moyenne pondérée (total
pondéré divisé par 24).
Le professeur en question étant aussi un programmeur
curieux, il décide d'écrire le programme dans quatre
langages de programmation différents et de comparer
les codes sources.
Le résultat obtenu nous montre que:
- une solution écrite en C sera la plus longue
puisque, la librarie standard étant peu fournie
à ce niveau il faudra traiter soit-même l'entrée
du tableau,
- La plus abordable est en Python, qui présente
l'avantage d'être lisible, facile et rapide.
- Un peu plus verbeux et rigide sera le code écrit en Go;
- Et bien que moins répandu, la solution la plus
succinte est obtenue avec ob-awk, tout désigné
pour ce genre de programme.
** Tests C :noweb
On fait par la suite usage de :noweb
pour faire les inclusions de sources.
Quelques vérifications préalables
s'imposent pour s'assurer que tout fonctionne
*** test 1
#+name: test1
#+begin_src C
int c;
c=42;
int test() {
return 1;
}
printf("%i\n",c);
#+end_src
#+results:
: 42
#+begin_src C
#include <stdio.h>
printf("%i\n",43);
test();
#+end_src
#+begin_src C
#include "stdlib.h"
#include "stdio.h"
<<test1>>
#+end_src
*** test 2
#+name: srcMyfunc
#+begin_src C
void myfunc() {
printf("print from function\n");
}
#+end_src
#+name: srcMain
#+begin_src C
int main(int argc,char **argv) {
printf("Hello World\n");
myfunc();
exit(0);
}
#+end_src
#+begin_src C
#include "stdlib.h"
#include "stdio.h"
<<srcMyfunc>>
<<srcMain>>
#+end_src
** Données
Les données sont dans un fichier texte organisé en tableau
come dans l'exemple suivant.
#+begin_src text
| BAACH Leila | 6 | 9 | 9 |
| BARREIRO Bruno | 9 | 0 | 10 |
| COURAULT Eva | 2 | 8 | 0 |
| DELL OVA Laura | 3 | 0 | 3 |
#+end_src
** Génération du fichier de données
A titre d'exemple, on génère à l'aide d'un script python un tableau dans un fichier
comprenant une liste de noms avec pour chacun trois notes.
#+name: TestSet
#+begin_src python
from random import randint
elv = """\
BAACH Leila
BARREIRO Bruno
COURAULT Eva
DELL OVA Laura
DUGUET Pierre
ESCANDE Thomas
GATTO Marianne
GUIRAUD Sarah
LHEUILLIER Margaux
LONGO Tania
MARTIN Caroline
MARTY Lea
MATTIA Louis
MORIN Jules
PASCALE Eva
PAYSSERAND Laura
RIBEIRO SOCORRO Jade
ROMERO Bastien
ROTY Elodie
RUTIGLIANO Marine
SAFOURCADE Morgane
ZOUINI Nada"""
carac = 3
with open("/tmp/eleves.txt","w") as f:
for e in elv.split('\n'):
el = (("| %s |" + (" %i |"*carac)) % (
e.strip(), *[randint(0,10) for x in range(carac)]))
print(el)
f.write(el+"\n")
#+end_src
#+name: notes-eleves
#+begin_src sh
cat /tmp/eleves.txt
#+end_src
** C
C n'est pas le langage le plus adapté à ce cas de figure,
compte tenu qu'il faudra traiter soit mêmes les entrées/sorties.
*** Fonctions C pré-requises
**** Supprimer les espaces superflus dans les chaînes avant conversion en entier
exemple : la chaine " 42 " est convertie en l'entier 42.
#+name: trimwhitespace
#+begin_src C
#include <string.h>
#define isspace(c) ((c)==' ' || (c)=='\t' || (c)=='\n')
char *trimwhitespace(const char *str)
{
char *end;
while(isspace((unsigned char)*str)) str++;
if(*str == 0)
return str;
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
end[1] = '\0';
return str;
}
#+end_src
**** Scinder une chaîne et récupérer les champs
#+name: champs
#+begin_src C
/**
Scinder une chaîne en champs
,**/
#include <stdlib.h>
#include <string.h>
const char* champ(char* line, const char * c, int num)
{
char * tmp = strdup(line);
const char* tok;
for (tok = strtok(tmp, c);
tok && *tok;
tok = strtok(NULL, c))
{
if (!--num) {
return tok;
}
}
return NULL;
}
#+end_src
*** test C
#+name: test
#+begin_src C
<<parse>>
<<trimwhitespace>>
#include <stdlib.h>
#include <stdio.h>
int franc, maths, higeo;
int main()
{
FILE *fp;
char str[60];
fp = fopen("/tmp/eleves.txt" , "r");
char line[1024];
while (fgets(line, 1024, fp))
{
char * tmp = strdup(line);
char * nom = getfield(tmp, "|", 1);
printf("%s ",nom);
tmp = strdup(line); franc = atoi(trimwhitespace(getfield(tmp, "|", 2)));
tmp = strdup(line);
maths = atoi(trimwhitespace(getfield(tmp, "|", 3)));
tmp = strdup(line);
higeo = atoi(trimwhitespace(getfield(tmp, "|", 4)));
printf("%i %i %i\n", franc, maths, higeo);
free(tmp);
}
fclose(fp);
}
#+end_src
#+results: test
#+begin_example
BAACH Leila 7 8 6
BARREIRO Bruno 6 9 4
COURAULT Eva 2 6 9
DELL OVA Laura 10 6 7
DUGUET Pierre 6 0 8
ESCANDE Thomas 3 10 7
GATTO Marianne 7 1 4
GUIRAUD Sarah 10 7 5
LHEUILLIER Margaux 8 9 5
LONGO Tania 5 4 7
MARTIN Caroline 6 8 9
MARTY Lea 3 3 2
MATTIA Louis 3 10 1
MORIN Jules 8 0 6
PASCALE Eva 5 10 7
PAYSSERAND Laura 0 0 3
RIBEIRO SOCORRO Jade 8 0 3
ROMERO Bastien 3 8 7
ROTY Elodie 4 5 9
RUTIGLIANO Marine 5 6 1
SAFOURCADE Morgane 1 2 9
ZOUINI Nada 4 8 5
#+end_example
*** C Ansi99
Compte tenu du format de nos données,
quelques fonctions sont préalablement nécéssaires.
#+name: BilanC
#+begin_src C
<<champs>>
<<trimwhitespace>>
#include <stdlib.h>
#include <stdio.h>
const struct Coeff {
int francais;
int maths;
int histgeo;
} coeff = {
.francais = 10,
.maths = 8,
.histgeo = 6,
};
int total_coeff;
typedef struct Eleve {
char * nom;
int francais, maths, histgeo, total;
float moy;
} Eleve;
Eleve ** tab180;
Eleve ** tab120;
Eleve ** tabinf;
const VSZ = 16;
void affiche_liste(Eleve ** eleves, int nbe) {
printf("|%-22s|%8s|%5s|%7s|%3s|%5s|\n",
"Nom","français","maths","histgeo","moy","total");
printf("|-\n");
for (int i=0; i<nbe; i++){
printf("|%-22s|%8d|%5d|%7d|%.1f|%5d|\n",
eleves[i]->nom,
eleves[i]->francais,
eleves[i]->maths,
eleves[i]->histgeo,
eleves[i]->moy,
eleves[i]->total);
}
}
int main()
{
FILE *fp; fp = fopen("/tmp/eleves.txt" , "r");
char line[128];
tab180 = malloc(VSZ*sizeof(void*));
tab120 = malloc(VSZ*sizeof(void*));
tabinf = malloc(VSZ*sizeof(void*));
int v180 = 1, v120 = 1, vinf = 1;
int n180=0, n120=0,ninf=0;
total_coeff = coeff.francais + coeff.maths + coeff.histgeo;
while (fgets(line, 128, fp)) {
Eleve * eleve = malloc(sizeof(Eleve));
eleve->nom = trimwhitespace(champ(line, "|", 1));
eleve->francais = atoi(trimwhitespace(champ(line, "|", 2)));
eleve->maths = atoi(trimwhitespace(champ(line, "|", 3))),
eleve->histgeo = atoi(trimwhitespace(champ(line, "|", 4))),
eleve->total =
eleve->francais * coeff.francais
+ eleve->maths * coeff.maths
+ eleve->histgeo * coeff.histgeo;
eleve->moy =(float) eleve->total / (float)total_coeff;
if(eleve->total >= 180) {
tab180[n180] = eleve;
n180++;
}else if(eleve->total >= 120) {
tab120[n120] = eleve;
n120++;
}else {
tabinf[ninf] = eleve;
ninf++;
}
if(n180 > VSZ*v180) {
realloc(tab180, v180 * VSZ * sizeof(void*));
v180++;
}
if(n120 > VSZ*v120) {
realloc(tab120, v120 * VSZ * sizeof(void*));
v120++;
}
if(ninf > VSZ*vinf) {
realloc(tabinf, vinf * VSZ * sizeof(void*));
vinf++;
}
}
printf("\ntab180\n");
affiche_liste(tab180,n180);
printf("\ntab120\n");
affiche_liste(tab120,n120);
printf("\ntabinf\n");
affiche_liste(tabinf,ninf);
fclose(fp);
}
#+end_src
#+attr: :align center
** Python
De manière plus concise :
#+name: BilanPython
#+begin_src python
coeff = {
"français":10,
"maths":8,
"histgeo":6
}
total_coeffs = sum(coeff.values())
tab180=[]
tab120=[]
tabinf=[]
with open("/tmp/eleves.txt") as f:
L = f.readline()
while L:
_,nom,francais,maths,histgeo,_ = L.split('|')
francais,maths,histgeo = int(francais), int(maths), int(histgeo)
eleve = {
"nom":nom,
"français": francais,
"maths": maths,
"histgeo": histgeo,
}
total = 0
for k in coeff.keys():
total += coeff[k] * eleve[k]
moy = float(total) / float(total_coeffs)
eleve.update( {
"moy": moy,
"total": total,
})
if(total>=180):
tab180.append(eleve)
elif(total >= 120):
tab120.append(eleve)
else:
tabinf.append(eleve)
L = f.readline()
def afficher_liste(li):
print(
"|{0:22s}|{1:9s}|{2:5s}|{3:7s}|{4:3s}|{5:5s}|".format(
"Nom","français","maths","histgeo","moy","total"
))
print("|-")
for eleve in li:
print(
"|{0:22s}|{1:8d}|{2:5d}|{3:7d}|{4:.1f}|{5:5d}|".format(
eleve["nom"],
eleve["français"],
eleve["maths"],
eleve["histgeo"],
eleve["moy"],
eleve["total"],
))
print("\ntab180")
afficher_liste(tab180)
print("\ntab120")
afficher_liste(tab120)
print("\ntabinf")
afficher_liste(tabinf)
#+end_src
** Go
#+name: BilanGo
#+begin_src go
import (
"fmt"
"strings"
"io/ioutil"
"strconv"
)
/* sépare une chaîne en un tableau de chaîne selon
un caractère */
func split(s string, r rune) []string {
return strings.FieldsFunc(s,
func(c rune) bool { return c == r })
}
type Eleve struct
{
nom string
notes map[string]int
total int
moy float32
}
var Coeff map[string]int
var tab180, tab120, tabinf []Eleve
func trim_int(s string) int {
i, err := strconv.Atoi(strings.TrimSpace(s))
if(err!=nil) { panic(err) }
return i
}
func main() {
Coeff := make(map[string]int)
Coeff["français"] = 10
Coeff["maths"] = 8
Coeff["histgeo"] = 6
var CoeffTotal = 0
for k := range Coeff {
CoeffTotal += Coeff[k]
}
list, _ := ioutil.ReadFile("/tmp/eleves.txt")
line := split(string(list), '\n')
for i := range(line) {
fields := split(line[i], '|')
elv := Eleve{
nom: strings.TrimSpace(fields[0]),
notes: map[string]int{
"français": trim_int(fields[1]),
"maths": trim_int(fields[2]),
"histgeo": trim_int(fields[3])},
moy:0,
total:0 }
for k := range elv.notes {
elv.total += Coeff[k] * elv.notes[k]
}
elv.moy = float32(elv.total) / float32(CoeffTotal)
if (elv.total >= 180) {
tab180 = append(tab180, elv)
}else if(elv.total >= 120) {
tab120 = append(tab120, elv)
}else{
tabinf = append(tabinf, elv)
}
}
fmt.Println("tab180")
Affiche_Tableau(tab180)
fmt.Println("tab120")
Affiche_Tableau(tab120)
fmt.Println("tabinf")
Affiche_Tableau(tabinf)
}
func Affiche_Tableau(eleves []Eleve) {
fmt.Printf( "|%-22s|%8s|%5s|%7s|%3s|%5s|\n",
"Nom","français","maths","histgeo","moy","total" )
for i := range eleves {
fmt.Printf("|%-22s|%8d|%5d|%7d|%.1f|%5d|\n",
eleves[i].nom,
eleves[i].notes["français"],
eleves[i].notes["maths"],
eleves[i].notes["histgeo"],
eleves[i].moy,
eleves[i].total)
}
}
#+end_src
** Awk
La solution la plus succinte et la mieux adaptée
au cas.
Pour retirer les confirmations d'exécution :
#+begin_src elisp
(defun my-org-confirm-babel-evaluate (lang body)
(not (or
(string= lang "python")
(string= lang "awk")
(string= lang "sh"))))
(setq org-confirm-babel-evaluate 'my-org-confirm-babel-evaluate)
(org-babel-do-load-languages
'org-babel-load-languages
'((awk . t)))
#+end_src
*** awk-sh
En appelant awk via le shell, le résultat tient en ces quelques lignes.
#+name: resultats-eleves
#+begin_src sh
awk '
BEGIN {printf "|Nom|Français|Maths|Histoire-géo|total|moyenne|\n|-"}
{ total= 10*$3 + 8*$4 + 6*$5;
if (total > '$critere')
printf "|%s|%d|%d|%d|%d|%.1f\n", $2, $3, $4, $5, total, total/24}'\
FS="|" $file
#+end_src
La numérotation du premier champ commence à 2, car les données
fournissent le séparateur "|" en début de ligne. On obtient déjà une
solution satisfaisante qui puisse être paramétrée
: #+call: resultats-eleves(180, "/tmp/eleves.txt")
*** ob-awk
#+results:
Ici on peut tirer parti des possibilités de babel pour indiquer les
paramètres, la source de données et avoir une coloration syntaxique.
Le résultat tient en 5 lignes:
#+name: resultats-élèves
#+begin_src awk
BEGIN { printf "|Nom|Français|Maths|Histoire-géo|total|moyenne|\n|-" }
{ total= 10*$3 + 8*$4 + 6*$5;
if (total > critere)
printf "|%s|%d|%d|%d|%d|%d\n", $2,$3,$4,$5, total, total/24}
#+end_src
Qui peut-être appelé ainsi
: #+CALL: resultats-eleves(critere=155)
Pour plus de flexibilité, on peut découper et
assembler le programme avec ob-awk + noweb.
Le découpage avec noweb ne sera visible que
dans les sources org, pas dans les exports html,
qui procède déjà à l'assemblage.
#+name: header-notes
#+begin_src awk
printf "|Élève|Français|Maths|Hist.Géo|Total|Moyenne\n|-";
#+end_src
#+name: print-line
#+begin_src awk
printf "|%s|%d|%d|%d|%d|%.1f\n", $2,$3,$4,$5, total, total/24
#+end_src
#+name: note-totale
#+begin_src awk
total = 10*$3 + 8*$4 + 6*$5;
#+end_src
#+name: resultats-eleves>
#+begin_src awk
BEGIN { printf "|Élève|Français|Maths|Hist.Géo|Total|Moyenne\n|-"; }
{ total = 10*$3 + 8*$4 + 6*$5;
if (total >= critere) printf "|%s|%d|%d|%d|%d|%.1f\n", $2,$3,$4,$5, total, total/24
}
#+end_src
: #+Call: resultats-eleves>(170)
C-c ^ SPC~ pour le tri