Next: Quelques éléments de Caml
Up: Un exemple simple
Previous: Vecteurs et tableaux
Caml est un langage fonctionnel: comme nous l'avons déjà vu, les fonctions forment les briques de base des programmes. En outre, les fonctions sont des valeurs primitives du langage qu'on manipule au même titre que les autres valeurs. Il est très facile de définir des fonctions qui manipulent des fonctions ou même de fabriquer des structures de données qui comportent des fonctions. Une fonction peut librement être prise en argument ou rendue en résultat, et il n'y a pas de restriction à son usage dans les structures de données.
En Caml, les fonctions sont des valeurs comme les autres.
Comme en mathématiques, une fonction a des arguments et rend un résultat qu'elle calcule avec une expression où intervient la valeur de ses arguments. Comme pour les autres valeurs, la définition d'une fonction est introduite par un mot clé let suivi du nom de la fonction et de la liste de ses arguments, ce qui nous donne typiquement let f x = ... pour une fonction à un argument et let f x1 x2 ... xn = ... pour une fonction à n arguments.
Voici un exemple de fonction des entiers dans les entiers:
let prochain x = if x mod 2 = 1 then 3 * x + 1 else x / 2;;
La fonction prochain renvoie 3x+1 si x est impair, et si x est pair. (On peut s'amuser à itérer cette fonction et à observer ses résultats successifs; on ne sait toujours pas démontrer qu'on obtient finalement 1, quelque soit l'entier de départ. Par exemple: 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1).
Définissons maintenant le prédicat even, qui teste la parité des entiers (c'est une fonction des entiers dans les booléens):
let even x = x mod 2 = 0;;
On remarque que les définitions de prochain et de even ne font pas intervenir de types: la signature des fonctions est implicite. Pourtant Caml dispose, comme Pascal, d'un typage fort, c'est-à-dire strict et vérifié complètement à la compilation. En effet, les types des arguments et du résultat des fonctions sont automatiquement calculés par le compilateur, sans intervention du programmeur, ni annotations de type dans les programmes. L'habitué de Pascal ou C pourra s'il le désire insérer des types dans ses programmes, à l'aide de contraintes de type. Une contrainte de type consiste en un morceau de programme mis entre parenthèses et décoré avec son type. Ainsi (x : int) impose que x soit un entier. Avec une contrainte sur son argument et son résultat la fonction even s'écrirait:
let even (x : int) = (x mod 2 = 0 : bool);;
Comme on le constate sur le programme du carré magique, les contraintes de type ne sont pas nécessaires, il n'est donc pas d'usage d'en mettre dans les programmes.
En Caml, le typage est à la charge du compilateur.
Lorsque l'argument ou le résultat d'une fonction sont sans intérêt, on le note alors (), l'unique valeur du type unit. Une telle fonction est souvent qualifiée de procédure au lieu de fonction, mais ce n'est qu'une distinction commode qui n'est pas faite par le langage. Par exemple, main est une procédure, et l'on constate qu'elle est définie exactement de la même manière que notre fonction prochain ou le prédicat even.
La fonction magique est elle aussi une procédure, elle construit un carré magique d'ordre n impair de la même façon qu'en Pascal ou C.
La fonction lire lit la taille du carré magique et fait quelques vérifications sur sa valeur. En cas de taille incorrecte, elle appelle la fonction erreur qui affiche un message et arrête le programme en erreur. Pour lire cette taille, elle appelle la procédure de lecture d'une ligne read_line, après avoir imprimé un message sur le terminal. La procédure d'impression formatée printf a pour premier paramètre une chaîne de caractères, délimitée par des guillemets. C'est le format qui indique comment imprimer les arguments qui suivent: on spécifie le type d'impression désiré, à l'aide de caractères symboliques, précédés de l'indicateur signifie qu'on doit imprimer une chaîne de caractères, et indications d'impression dans le format doit être corrélé avec l'ordre des arguments à imprimer. Dans la procédure imprimer qui imprime le tableau a, l'indication caractères, cadré à droite.
Enfin, la procédure main est la procédure principale du programme qui fait appel successivement aux différentes procédures, dans un ordre simple et compréhensible. La méthode qui consiste à définir de petites fonctions, qu'on appelle ensuite dans la fonction principale est un principe de programmation structurée. Elle améliore la lisibilité et facilite les modifications, mais n'est pas une obligation du langage. Nous aurions pu définir toutes les fonctions locales à la fonction main, mais en général ce style est mauvais, car on mélange le coeur algorithmique du programme (la procédure magique) avec des détails annexes comme l'impression et la lecture. Remarquons qu'il faut appeler explicitement le programme principal, ce que fait la ligne main ();; À défaut, la procédure main serait définie mais pas appelée, et le programme ne ferait rien.
1/11/1998