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 endqui 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ù
compteur
est le nom du compteur,expr1
etexpr2
sont des entiers, valeurs initiale et finale.expr3
est 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
.