Résumé des épisodes précédents :
dans un thread précédent sur la qualité en OCaml, nous avons écrit, comme tout un chacun lors de ses premiers pas en Caml, une fonction factorielle toute bête :
C'est court, c'est simple, ça fonctionne, on pourrait s'en satisfaire. Or si l'on creuse un peu, les choses se compliquent :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 # let rec factorial n = if n=0 then 1 else n * factorial (n - 1);; val factorial : int -> int = <fun> # factorial 5;; - : int = 120
pire (?) :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 # factorial 25;; - : int = -71303168
et ne me parlez pas de :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 # factorial 50;; - : int = 0
Bon, c'est alors que SpiceGuid nous propose :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 # factorial (-1);; Stack overflow during evaluation (looping recursion?).
formidable, nous avons résolu le cas factorial (-1) qui renvoie maintenant :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 let rec factorial n = assert(n >= 0); match n with | 0 -> 1 | m -> m * factorial (m - 1);; val factorial : int -> int = <fun>
SpiceGuid, j'aime particulièrement cette approche que j'ai un (tout petit) peu expérimenté dans Eiffel via le Design by Contract, la voie royale pour la qualité dans les langages impératifs.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 # factorial (-1);; Exception: Assert_failure ("", 92, 4).
je voudrais bien maintenant éviter les problèmes que posentcomme ceci par exemple :
Code : Sélectionner tout - Visualiser dans une fenêtre à part factorial 25
le compilateur rejette cela avec un grand mugissement dont la signification m'est, à cette heure tardive, inaccessible. Je suspecte une écriture trop "impérative" à son goût.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 let rec factorial n = assert(n >= 0); match n with | 0 -> 1 | m -> let result = m * factorial (m - 1) in result; assert(result > 0);;
Pensons fonctionnel, toujours plus fonctionnel - après bien des tâtonnements :me renvoie bien les "assertion failures" ad hoc.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 let rec factorial n = assert (n >= 0); match n with | 0 -> 1 | m -> let result = m * factorial (m - 1) in if result >0 then result else assert false;;
Néanmoins le code n'est pas bien joli, en particulier j'aimerais tellement que l'assert de "sortie" soit aussi élégamment disposé que le premier...
Ahh, la beauté (empoisonnée) des "require" et "ensure" (* =assert *) d'Eiffel dans :
Eiffel génère une signature de chaque fonction/procédure qui reprend les "require" et "ensure", ce qui est très précieux pour en documenter l'usage.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 factorial: NUMBER is require is_abstract_integer; is_positive local n: NUMBER; do ... end; ensure Result.is_abstract_integer; Result.is_positive end;
Quelqu'un a une idée pour rendre ce code joli dans le chameau ?
Partager