L'expression if expr1 then expr2 else expr3 où expr1 est une expression de type bool, et où les expressions expr2 et expr3 sont forcément de même type, constitue une structure décisionnelle. Lorsque la condition expr1 vaut true, la structure est équivalente à expr2, et dans le cas contraire lorsque expr1 vaut false, la structure est équivalente à expr3.
D'abord dans le cas où expr2 et expr3 sont de type unit :
let x = read_int() in if x >= 0 then print_string "positif" else print_string "négatif"
Ici, la condition est x >= 0. Les expressions print_string "positif" et print_string "négatif" sont toutes les deux de type unit.
Lorsque les expressions expr2 ou expr3 sont construites comme juxtapositions de plusieurs expressions, il faut délimiter le bloc. Cela se fait de préférence avec begin et end, bien qu'on puisse aussi utiliser des parenthèses en théorie.
Voici l'exemple précédent dans lequel on a rajouté l'affichage de la valeur absolue du nombre lorsque celui-ci est négatif :
let x = read_int() in if x >= 0 then print_string "positif" else begin print_string "négatif, de valeur absolue : "; print_int (abs x) end
Maintenant dans le cas où expr2 et expr3 ne sont pas de type unit : ces deux expressions doivent être de même type, et ce type est alors celui de l'ensemble de la structure if.
Par exemple, lorsque x est défini, l'expression :
if x >= 0 then "positif" else "négatif"
est de type string, puisque "positif" et "négatif" sont tous les deux de type string.
Ainsi :
let x = read_int() in print_string (if x >= 0 then "positif" else "négatif");
affiche le signe de l'entier x donné par l'utilisateur.
On peut aussi associé un nom à l'expression formée par la structure if :
let x = read_int() in let signe = if x >= 0 then "positif" else "négatif" in print_string signe
Dans ce cas, il sera préférable de présenter cela sur plusieurs lignes, comme on présente les fonctions :
let x = read_int() in
let signe =
if x >= 0
then "positif"
else "négatif"
in
print_string signe
Voyons un cas où expr2 et expr3 où sont des juxtapositions d'expressions, d'un autre type que unit. En fait on l'a déjà utilisé, pour le jeu ``C'est plus, c'est moins'' en récursif :
if cible > essai then begin print_string "C'est plus\n"; succ (cible jeu) end else if cible < essai then begin print_string "C'est moins\n"; succ (cible jeu) end else begin print_string "C'est gagné !\n"; 1 end ;
Terminons avec le bloc du else implicite.
L'expression if expr1 then expr2 est équivalente à if expr1 then expr2 else (). Par conséquent, expr1 doit être de type bool et expr2 doit être de type unit.
Par exemple :
let x = read_int() in if x > 0 then print_string "positif"
est équivalent à
let x = read_int() in if x > 0 then print_string "positif" else ()
Si expr2 n'est pas de type unit, cela provoque une erreur de type. Illustration :
let x = read_int() in print_string (if x > 0 then "positif")
File "test.ml", line 2, characters 28-37: This expression has type string but is here used with type unit
Erreur sur la chaîne "positif" qui n'est pas de type unit.
Le ET du calcul booléen, opérateur &&, est en fait équivalent à une structure avec des if. Cherchons l'équivalent de expr1 && expr2. Si expr1 est vrai, alors si expr2 est aussi vrai, le résultat est vrai. Mais si expr1 ou expr2 est faux, le résultat est faux. Ceci s'écrit :
if expr1 then
begin
if expr2
then true
else false
end
else
false
Mais l'expression if expr2 then true else false est équivalente à la valeur de expr2 directement. Donc en simplifiant le code et la présentation, il reste :
if expr1 then expr2 else falseConclusion :
L'expression expr1 && expr2 où les deux expr1 et expr2 sont de type bool est équivalente à if expr1 then expr2 else false.
Il y a une observation importante à faire : si expr1 est évalué à faux, alors expr2 ne sera même pas évalué, puisqu'on est déjà sûr que le résultat de expr1 && expr2 est faux.
Pour vérifier cela, on va utiliser des expressions qui affichent quelque chose lorsqu'elles sont évaluées. Pour commencer, on va ainsi prendre pour expr1 l'expression print_string "expr1 "; true et pour expr2 l'expression print_string "expr2 "; true, et voir ce qu'il se passe en affichant le résultat de expr1 && expr2 :
let b = (print_string "expr1 "; true) && (print_string "expr2 "; true) in print_string (string_of_bool b);Ce code affiche
expr1 expr2 vrai. On en déduit que expr1 a été évalué, puis expr2, et que expr1 && expr2 était vrai.
Regardons maintenant ce qu'il se passe si on fait que expr2 soit faux :
let b = (print_string "expr1 "; false) && (print_string "expr2 "; true) in print_string (string_of_bool b);Ce code affiche cette fois
expr1 faux. On en déduit que expr1 a été évalué, mais pas expr2, et que le résultat de expr1 && expr2 était faux.
On va calquer le raisonnement effectué pour le ET pour traiter le OU. L'expression expr1 || expr2 vaut vrai lorsque l'une ou l'autre des deux expressions est vraie. Traduisons cela avec des if. Si expr1 est vrai, on est sûr que le résultat est vrai. Sinon il faut regarder si expr2 est vrai ou faux.
if expr1 then
true
else
begin
if expr2
then true
else false
end
qui se simplifie tout de suite en :
if expr1 then true else expr2Conclusion :
L'expression expr1 || expr2 où les deux expr1 et expr2 sont de type bool est équivalente à if expr1 then true else expr2.
Vous pouvez vérifier que lorsque la condition expr1 est vrai, la condition expr2 n'est même pas évaluée.
for compteur = expr1 to expr2 do expr3 doneoù
compteurest le nom du compteur,expr1etexpr2sont des entiers, valeurs initiale et finale.expr3est une expression de type unit où l'on peut utiliser le nom du compteur, le corps de la boucle,
forme une expression de type unit, qui se comporte comme la répétition de expr3 pour toutes les valeurs de compteur comprises entre expr1 et expr2 inclus. S'il n'existe pas de telle valeur pour le compteur, alors la boucle ne fait rien et est équivalente à ().
Remarque : les valeurs initiales et finales du compteur, expr1 et expr2, ne sont évaluées qu'une seule fois au début. La preuve :
for i = (print_string "expr1 "; 1) to (print_string "expr2 "; 5) do print_int i; done;
affiche expr1 expr2 12345.
On a également la règle suivante.
while expr1 do expr2 done
où expr1 (la condition de la boucle) est de type bool et expr2 (le corps de la boucle) de type unit est une expression de type unit, qui se comporte comme la répétition de expr2 tant que l'expression expr1 est évaluée à vrai.
let i = ref 1 in while (print_string "expr1 "; !i <= 5) do print_int !i; incr i; done;
affiche "expr1 1 expr1 2 expr1 3 expr1 4 expr1 5 expr1". Ainsi l'expression expr1 qui détermine l'exécution de la boucle est exécuté tout au début, mais aussi tout à la fin : la fois où elle est évaluée à faux.
La définition d'une fonction :
let arg_de_la_fonction arg_1 arg_2 ... arg_N = expr1 in
où arg_de_la_fonction et tous les arg_i sont des noms, forme une expression de type :
type(arg_1) -> type(arg_2) -> ... -> type(arg_N) -> type(expr_1)
les types des arguments étant déduits de l'utilisation qui en est faîte dans expr1.
L'expression :
nom_de_fonction expr_1 expr_2 ... expr_N
lorsque nom_de_fonction est associé à une fonction de type
type_arg_1 -> type_arg_2 -> ... -> type_arg_N -> type_retour
est une expression de type type_retour, à condition que chaque expr_i soit de type type_arg_i.
France-IOI
