Les fonctions
C'est parti pour apprendre les fonctions qui sont totalement indispensables en caml.
Comment créer des fonctions ?
Pour créer des fonctions, rien de plus simple, il suffit de placer le mot let devant la définition de la fonction, suivi du Nomfonction ainsi qu'un certain(s) nombre(s) de variables. En respectant cette structure, Ocaml reconnaîtra que c'est une fonction (il reconnaît le nombre et le type d'arguments, ainsi que son retour).
Exemple:
let Nomfonction Nomvariable = instruction;;
let Nomfonction = fun Nomvariable -> instruction;;(* Autre façon d'écrire la même fonction *)
Exemple : une fonction faisant la somme de deux variables.
let somme x y = x + y;;
val somme : int -> int -> int
Le Typage des Fonctions
Selon le code de vos fonctions, l'interpréteur ou le compilateur associeront des types à vos variables.
Arrêtons nous une minute sur la signature de notre fonction somme de tout à l'heure :
val somme : int -> int -> int est la signature de notre fonction somme (c'est la réponse de l'interpréteur à vos instructions, dès que vous créez une entité, l'interpréteur renvoie une signature).
On reconnaît une fonction en regardant le dernier terme de la signature. Maintenant que l'on sait que c'est une fonction, on va chercher à savoir le type et le nombre(s) d'argument(s) qu'elle prend ainsi que le retour la fonction. On va étudier la partie: int -> int -> int
Cette partie là, indique le type des arguments ainsi que le retour de la fonction. Les fonctions Caml ne peuvent renvoyer qu'une seule valeur. On en déduit que la fonction prend deux arguments, les deux premiers int représentent les types des deux arguments de somme et le 3ème int représente le type de retour de somme.
D'une manière générale la connaissance du typage de vos fonctions vous évitera bien des erreurs.
Cela vous rendra également plus efficace pour analyser vos éventuels messages d'erreurs.
Évaluez (c'est-à-dire : donner la signature) des fonctions suivantes. Attention, l'utilisation d'un quelconque interpréteur est interdite si vous n'avez pas planché un peu sur le problème avec un crayon et un bout de papier (sinon, ce n'est pas instructif :-|)
let f = fun x y -> x mod y > y * 2 + 5;;
let f a = float_of_int a;;
let f g h x y z = (g (x,y) ) *. (h y z) ;;(* C'est un peu tordu, mais vous pouvez la réussir *)
Quand vous avez créé une fonction dans l'interpréteur, pour retrouver sa signature, saisissez tout simplement : le nom de votre fonction
somme;;
val somme : int -> int -> int
int_of_float;;
- : float -> int
Le Type Quelconque
Vous vous demandez ce que signifie 'a ou 'b voir 'c dans les signatures de vos fonctions ! ! !
Il arrive que les paramètres (et même les retours de fonction) "puissent" prendre plusieurs type différents. Je m'explique:
let egale x y = x = y;;
val egale : 'a -> 'a -> bool =
egale est une fonction banale qui vérifie que deux variables sont égales ou non. Elle prend deux paramètres et renvoie de toute façon un bool. Ces deux paramètres sont forcément du même type (dans la signature on voit 'a -> 'a, en effetc'est imposé par l'oprérateur ). La particularité de egale est que les deux paramètres peuvent être de type:
* int et int
* ou float et float
* ou char et char
* ou même string et string.
egale "ocaml" "caml";; (* On apelle egale avec deux string *)
- : bool = false
egale 'a' 'a';; (* On apelle egale avec deux char*)
- : bool = true
egale 2. 4.;; (* On apelle egale avec deux float *)
- : bool = false
egale 1 18976;; (* On apelle egale avec deux int *)
- : bool = false
egale 3 'e';; (* On appelle egale avec un int et un char . . . Erreur *)
Characters 8-11
egale 3 'e' ;;
^^^
Error: This expression has type char but is here used with type int
(* Ocaml attend un deuxième int à la place de 'e' *)
Les Conditions
Très vite, vous vous rendrez compte des limites des programmes sans conditions. En effet, comment créer un menu si on ne peut offrir un réponse différente pour chaque choix de l'utilisateur. En Caml, il existe deux façons d'inclure des conditions : en utilisant des if/else ou des match with.
Les IF THEN ELSE
En anglais, if signifie si, then signifie alors et else signifie sinon. if est suivi d'une condition (qui renvoie un booléen), si la condition est vraie alors on passe à l'instruction qui suit then, sinon à l'instruction qui suit else.
Le if et le then sont un duo indissociable. Si vous oubliez l'un des deux, votre code ne sera pas utilisable.
let f x = if x = 3 print_int 3 else print_int 1;;
Error: Syntax error
(* Le ELSE sera souligné mais l'erreur sera du au fait que le THEN sera oublié *)
Reprenons l'exemple du menu.
print_string "Que voulez-vous faire aujourd'hui ? \n";
print_string "1.Glander \n";
print_string "2.Apprendre/reviser le caml \n";
let choix = read_int() in
if choix = 2 then "Tu es au bon endroit. :-) "
else "mode apprentissage passif enclenche"
;;
Cependant, si l'on tape 3 au moment où le choix est réalisé, on obtient la réponse "mode apprentissage passif enclenche" sans avoir choisi l'option glander. On utilise un else if : celui-ci s'utilise comme un if mais il doit être situé près un then et on peut en mettre autant de fois que nécessaire. Par contre, il ne peut représenter en aucun cas le choix par défaut (cela est réservé au else).
let choix = read_int() in
if choix = 2 then "Tu es au bon endroit. :-) "
else if choix = 1 then"mode apprentissage passif enclenche"
else "Ni un glandeur ni un bosseur... quelle genre Etudiant Non Identifie es-tu ?
Match With What ? (part 1)
let choix nb = match nb with
|0 → 0
|a → 1;;
val choix : int -> int
Cette fonction est basique, elle renvoie la valeur 0 si 0 est entré en paramètre et dans tous les autres cas elle renvoie 1.
Voici la même fonction écrite avec un if then else :
let choix nb =
if nb = 0 then 0
else 1 ;;
Ce qu'il y a de nouveau ici c'est le match with. En anglais ce bout de code signifie : cherche une correspondance entre nb et les valeurs qui suivent. Vous l'aurez compris, on peut écrire la même chose avec un if then else qu'avec un match with, le gros avantage du match with c'est que c'est tout de suite plus lisible et c'est très facile à modifier LOL ! Si dans la partie correction des exercices Ocaml vous voyez des if then else ou des match with, ne vous affolez pas, c'est simplement que j'ai fait ces corrections de plusieurs manières ;-) !
Pour les cas généraux **et** si vous n'avez pas besoin de réutiliser les éléments du match, on peut utiliser "_".
On dit que l'on réalise un filtrage (on isole la variable sur laquelle on doit agir).
Reprenons l'exemple de la fonction choix \ mais en utilisant le "_".
let choix nb = match nb with
|0 → 0
|_ → 1;;
Si nb est différent de 0 alors on renvoie 1, l'underscore sert à ça.
Il gère tous les cas pour lesquels il n'y a pas de valeur précise, comme 0 ici.
* Un Menu Complet:
print_string "Que voulez-vous faire aujourd'hui ? \n";
print_string "1. Glander \n";
print_string "2. Apprendre/reviser le caml \n";
print_string "3. Aller a la piscine \n";
print_string "4. Manger une poire \n";
print_string "5. Embeter Ramy \n";
let choix = read_int() in match choix with
|1 -> print_string "Arrete de glander ! ! !\n"
|2 -> print_string "Bienvenue :-) \n"
|3 -> print_string "Mais y a pas de piscine ici . . . \n"
|4 -> print_string "On vend pas de poire au snack ! \n"
|5 -> print_string "Ça fait du bien, hein ? \n"
|_ -> print_string "Vous devez saisir une valeur entre 1 et 5 ! \n"
;;
Essayez le ;-) !
* Autre exemple:
Créons une fonction qui renvoie le troisième élément d'un triplet
let acces_element3 x = match x with
|(a,b,c) -> c
Il est impossible de réaliser cette fonction avec des if then else \ car il n'existe pas de fonction prédéfinie renvoyant un élément d'un triplet (ou d'un n-uplet d'une manière générale).
Pas de Paramètre
Il arrive que des fonctions ne prennent pas de paramètre (oui ça existe 8-o ). On en a déjà vu plusieurs, print_newline par exemple.
Comment appeler une fonction qui ne prend pas d'argument ?
print_newline ;;
- : unit -> unit =
Ici, nous n'avons pas appeler la fonction print_newline, en fait on a juste "demandé" à l'interpréteur de nous renvoyer la signature de print_newline.
Il suffit d'appeler la fonction avec (), en Caml ça signifie "Pas d'argument" :-\ !
print_newline () ;;
(* Ça fonctionne, on remarque le saut de ligne ! *)
- : unit = ()
C'est aussi simple que ça ^_^ ! Prenez l'habitude de le faire (y compris dans les autres langages de programmation que vous apprendrez plus tard !) Petite amélioration du programme HelloWorld:
let helloWorld () =
print_string ("Comment vous appelez-vous ?\n");(* \n dans une chaîne de caractère est un retour à la ligne ! *)
let nom = read_line () in
print_string ("Bonjour " ^ nom ^ ", Bienvenue dans ce tutoriel.\n\n");;
val helloWorld : unit -> unit =
Remarque : read_line est aussi une fonction qui ne prend pas d'argument.
Voilà, helloWorld est une fonction qui ne prend pas d'argument !
helloWorld () ;;
Comment vous-appelez-vous ?
KuK
Bonjour KuK, Bienvenue dans ce tutoriel.
- : unit = ()
helloWorld renvoie unit, c'est-à-dire rien.
Annexe: Fonctions Prédéfinies
* Pour savoir comment appeler une fonction (avec quels arguments !), tapez juste le nom de la fonction:
nomDeLaFonction ;;
- : signatureDeLaFonction =
print_string ;;
- : string -> unit =
En fait, ça vous renvoie la signature de la fonction et grâce à ça vous pouvez comprendre comment appeler cette fonction. Je vous l'ai déjà dit, mais je préfère le rappeler juste avant ce tableau.
* Lorsqu'on appelle une fonction avec () cela signifie qu'elle ne prend pas de paramètre ! Prenez l'habitude de le faire quand vous appelez une fonction (ou même quand vous créez vos fonctions).
* Une fonction de Sortie est une fonction qui permet de faire un affichage (dans le terminale/console par exemple).
* Une fonction d'Entrée est une fonction permet à l'utilisateur de faire entrée une donnée dans le programme (un int, float, char, ...). Ces fonctions sont utiles si vous voulez faire une "Pause" dans votre programme.
Voici un tableau regroupant une liste non exhaustive des fonctions prédéfinies dans Ocaml (mais qui suffit amplement):
Fonction | Utilité |
---|---|
Sortie | Fonctions d'Affichage |
print_char | Affiche un caractère à l'écran, prend un char en argument. |
print_string | Affiche une chaîne de caractères à l'écran, prend un string en argument. |
print_newline () | Saute une ligne à l'écran, pas d'argument. |
print_endline () | Affiche une chaîne de caractères à l'écran avec un retour à la ligne, pas d'argument. |
print_int | Affiche un entier à l'écran, prend un int en argument. |
print_float | Affiche un float à l'écran, prend un float en argument. |
Entrée | Fonctions de Saisie. |
read_char () | Saisie d'un char par l'utilisateur. |
read_line () | Saisie d'une string par l'utilisateur. |
read_int () | Saisie d'un int par l'utilisateur. |
read_float () | Saisie d'un float par l'utilisateur. |
Conversions | Convertions entre types. |
int_of_float | Convertit un float en int. |
float_of_int | Convertit un int en float. |
int_of_char | Convertit un char en int. |
char_of_int | Convertit un int en char. |
int_of_string | Convertit un string en int (à n'utiliser qu'avec une chaîne de caractères comprenant que des chiffres). |
string_of_int | Convertit un int en string. |
bool_of_string | Convertit une chaîne de caractères en bool (il faut que la chaîne de caractères soit "true" ou "false"). |
string_of_bool | Convertit un bool en string (donc ne peut renvoyer que "true" ou "false"). |
Mathématiques | Fonctions Mathématiques. |
mod | Donne le modulo de deux entiers (c'est-à-dire le reste de la division euclidienne). |
ceil | Arrondit un float au supérieur. |
floor | Arrondit un float à l'inférieur. |
sqrt | Renvoie la racine carré d'un float. |
exp | Renvoie l’exponentiel d'un float. |
log | Renvoie le logarithme népérien d'un float. |
log10 | Renvoie le logarithme en base 10 d'un float. |
cos | Renvoie le cosinus d'un float. |
sin | Renvoie le sinus d'un float. |
tan | Renvoie la tangente d'un float. |
acos | Renvoie l'arc cosinus d'un float. |
asin | Renvoie l'arc sinus d'un float. |
atan | Renvoie l'arc tangente d'un float. |
Aléatoire | Les Nombres Pseudo-aléatoires. |
Random.self_init () | Initialise le générateur de nombres aléatoires (à mettre impérativement avant de générer un nombre avec Random.int par exemple). |
Random.int | Renvoie un int aléatoire entre 0 et l'entier avec lequel on l'appelle. |
Random.float | Renvoie un foat aléatoire entre 0. et le float avec lequel on l'appelle. |
Pour savoir comment appeler une fonction (avec quels arguments !), tapez juste le nom de la fonction:
nomDeLaFonction ;;
- : signatureDeLaFonction =
print_string ;;(* Pour connaitre le type de la fonction : print_string *)
- : string -> unit =
En fait, ça vous renvoie la signature de la fonction et grâce à ça vous pouvez comprendre comment appeler cette fonction.
Je vous l'ai déjà dit, mais je préfère le rappeler juste après ce tableau.
Lorsqu'on appelle une fonction avec () cela signifie qu'elle ne prend pas de paramètre !
Prenez l'habitude de le faire quand vous appelez une fonction
(ou même quand vous créez vos propres fonctions).
Assurez-vous de bien maîtriser cette partie du tutoriel (les fonctions), parce que la prochaine étape c'est la Récursivité où il est primordial de connaître les fonctions.