On utilise la notation A B pour dénoter les fonctions de l'ensemble A dans l'ensemble B et par exemple int -> int est le type de la fonction fact ci-dessus. La valeur d'une fonction est une valeur fonctionnelle, notée conventionnellement <fun>. Remarquons à nouveau que toute définition lie un identificateur à une valeur; ainsi fact possède une valeur et il s'évalue normalement: #fact;;
- : int -> int = <fun>
À l'occasion de la définition de fact, on observe aussi que les fonctions récursives doivent être introduites par le mot-clé let rec, pour signaler au système qu'on fait référence à leur nom avant leur définition effective.
<BLOCKQUOTE>
</BLOCKQUOTE>Pour terminer sur les fonctions, citons l'existence des fonctions anonymes, c'est-à-dire des valeurs fonctionnelles qui n'ont pas de nom. On les introduit avec le nouveau mot clé function et la construction function x -> ... Par exemple:
#(function x -> x + 1);;
- : int -> int = <fun>
#(function x -> x + 1) 2;;
- : int = 3
Lorsqu'on donne un nom à une fonction anonyme, on définit alors très logiquement une fonction ``normale'': #let successeur = (function x -> x + 1);;
successeur : int -> int = <fun>
#successeur 2;;
- : int = 3
On utilise la plupart du temps les fonctions anonymes en argument des fonctionnelles que nous décrivons maintenant.
Fonctionnelles
Il n'y a pas de contraintes sur les arguments et résultats des procédures et des fonctions: les arguments et résultats fonctionnels sont donc autorisés. Une bonne illustration consiste à écrire une procédure de recherche d'une racine d'une fonction sur un intervalle donné. La procédure zéro prend une fonction f en argument (et pour cette raison on dit que zéro est une fonctionnelle). Définissons d'abord une fonction auxiliaire qui calcule la valeur absolue d'un flottant. let abs x = if x >= 0.0 then x else -. x;;
Nous notons à l'occasion que les nombres flottants comportent obligatoirement un point, même 0. Les opérations sur les nombres flottants sont également distinctes de celles des nombres entiers, puisqu'elles sont systématiquement suffixées par un point (sauf les comparaisons). Notre fonctionnelle s'écrit alors: let trouve_zéro f a b epsilon max_iterations =
let rec trouve x y n =
if abs (y -. x) < epsilon || n >= max_iterations then x
else
let m = (x +. y) /. 2.0 in
if (f m > 0.0) = (f x > 0.0)
then trouve m y (n + 1)
else trouve x m (n + 1) in
trouve a b 1;;
let zéro f a b = trouve_zéro f a b 1.0E-7 100;;
Remarquons la définition locale de la fonction récursive trouve, qu'on applique ensuite avec les arguments a, b et 1. Si on a des difficultés à comprendre ce style fonctionnel, voici la même fonction en version impérative (avec une boucle while que nous verrons plus loin): let zéro f a b =
let epsilon = 1.0E-7
and nmax = 100 in
let n = ref 1
and m = ref ((a +. b) /. 2.0) in
let x = ref a
and y = ref b in
while abs (!y -. !x) > epsilon && !n < nmax do
m := (!x +. !y) /. 2.0;
if (f !m > 0.0) = (f !x > 0.0)
then x := !m
else y := !m;
n := !n + 1
done;
!x;;
Le type inféré par Caml pour zéro est (float -> float) -> float -> float -> float qui indique bien que le premier argument est une fonction des flottants dans les flottants. Utilisons la fonctionnelle zéro pour trouver un zéro de la fonction logarithme entre 1/2 et 3/2:
#open "printf";;
#let log10 x = log x /. log 10.0;;
log10 : float -> float = <fun>
#printf "le zéro de log10 est %f\n" (zéro log10 0.5 1.5);;
le zéro de log10 est 1.000000
- : unit = ()
Les arguments fonctionnels sont naturels et rendent souvent l'écriture plus élégante. Il faut noter que l'efficacité du programme n'en souffre pas forcément, surtout dans les langages fonctionnels qui optimisent la compilation des fonctions et de leurs appels.
B.2.2 Symboles, séparateurs, identificateurs
Les identificateurs sont des séquences de lettres, de chiffres, d'apostrophes et de soulignés commençant par une lettre. Les identificateurs sont séparés par des espaces, des caractères de tabulation, des retours à la ligne ou par des caractères spéciaux comme +, -, *. Certains identificateurs sont réservés pour des mots clés de la syntaxe, comme and, type, begin, end, while, ...
Nous abandonnons la convention adoptée en Pascal et C dans ce cours qui consiste à commencer par une majuscule les noms de constantes et par une minuscule les noms de variables. En Caml, toutes les variables ont une valeur constante (au sens de Pascal et de C) et devraient donc commencer par une majuscule. Nous préférons utiliser les minuscules comme première lettre des variables. (Seuls les noms de constructeurs des types somme et les exceptions commenceront par une majuscule.)
- : int -> int = <fun>
À l'occasion de la définition de fact, on observe aussi que les fonctions récursives doivent être introduites par le mot-clé let rec, pour signaler au système qu'on fait référence à leur nom avant leur définition effective.
<BLOCKQUOTE>
Les fonctions récursives doivent être introduites par le mot-clé let rec. |
#(function x -> x + 1);;
- : int -> int = <fun>
#(function x -> x + 1) 2;;
- : int = 3
Lorsqu'on donne un nom à une fonction anonyme, on définit alors très logiquement une fonction ``normale'': #let successeur = (function x -> x + 1);;
successeur : int -> int = <fun>
#successeur 2;;
- : int = 3
On utilise la plupart du temps les fonctions anonymes en argument des fonctionnelles que nous décrivons maintenant.
Fonctionnelles
Il n'y a pas de contraintes sur les arguments et résultats des procédures et des fonctions: les arguments et résultats fonctionnels sont donc autorisés. Une bonne illustration consiste à écrire une procédure de recherche d'une racine d'une fonction sur un intervalle donné. La procédure zéro prend une fonction f en argument (et pour cette raison on dit que zéro est une fonctionnelle). Définissons d'abord une fonction auxiliaire qui calcule la valeur absolue d'un flottant. let abs x = if x >= 0.0 then x else -. x;;
Nous notons à l'occasion que les nombres flottants comportent obligatoirement un point, même 0. Les opérations sur les nombres flottants sont également distinctes de celles des nombres entiers, puisqu'elles sont systématiquement suffixées par un point (sauf les comparaisons). Notre fonctionnelle s'écrit alors: let trouve_zéro f a b epsilon max_iterations =
let rec trouve x y n =
if abs (y -. x) < epsilon || n >= max_iterations then x
else
let m = (x +. y) /. 2.0 in
if (f m > 0.0) = (f x > 0.0)
then trouve m y (n + 1)
else trouve x m (n + 1) in
trouve a b 1;;
let zéro f a b = trouve_zéro f a b 1.0E-7 100;;
Remarquons la définition locale de la fonction récursive trouve, qu'on applique ensuite avec les arguments a, b et 1. Si on a des difficultés à comprendre ce style fonctionnel, voici la même fonction en version impérative (avec une boucle while que nous verrons plus loin): let zéro f a b =
let epsilon = 1.0E-7
and nmax = 100 in
let n = ref 1
and m = ref ((a +. b) /. 2.0) in
let x = ref a
and y = ref b in
while abs (!y -. !x) > epsilon && !n < nmax do
m := (!x +. !y) /. 2.0;
if (f !m > 0.0) = (f !x > 0.0)
then x := !m
else y := !m;
n := !n + 1
done;
!x;;
Le type inféré par Caml pour zéro est (float -> float) -> float -> float -> float qui indique bien que le premier argument est une fonction des flottants dans les flottants. Utilisons la fonctionnelle zéro pour trouver un zéro de la fonction logarithme entre 1/2 et 3/2:
#open "printf";;
#let log10 x = log x /. log 10.0;;
log10 : float -> float = <fun>
#printf "le zéro de log10 est %f\n" (zéro log10 0.5 1.5);;
le zéro de log10 est 1.000000
- : unit = ()
Les arguments fonctionnels sont naturels et rendent souvent l'écriture plus élégante. Il faut noter que l'efficacité du programme n'en souffre pas forcément, surtout dans les langages fonctionnels qui optimisent la compilation des fonctions et de leurs appels.
B.2.2 Symboles, séparateurs, identificateurs
Les identificateurs sont des séquences de lettres, de chiffres, d'apostrophes et de soulignés commençant par une lettre. Les identificateurs sont séparés par des espaces, des caractères de tabulation, des retours à la ligne ou par des caractères spéciaux comme +, -, *. Certains identificateurs sont réservés pour des mots clés de la syntaxe, comme and, type, begin, end, while, ...
Nous abandonnons la convention adoptée en Pascal et C dans ce cours qui consiste à commencer par une majuscule les noms de constantes et par une minuscule les noms de variables. En Caml, toutes les variables ont une valeur constante (au sens de Pascal et de C) et devraient donc commencer par une majuscule. Nous préférons utiliser les minuscules comme première lettre des variables. (Seuls les noms de constructeurs des types somme et les exceptions commenceront par une majuscule.)