Premiers pas en OCaml/Fonctions

Début de la boite de navigation du chapitre

Ce chapitre est le plus important de la leçon. N'hésitez pas à le relire plusieurs fois et effectuer l'exercice dédié aux fonctions.

Fonctions
Icône de la faculté
Chapitre no 5
Leçon : Premiers pas en OCaml
Chap. préc. :Opérations arithmétiques
Chap. suiv. :Fonctions utilitaires
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Premiers pas en OCaml : Fonctions
Premiers pas en OCaml/Fonctions
 », n'a pu être restituée correctement ci-dessus.

Convention de nommage

modifier

Vous verrez à plusieurs reprises dans les définitions les notations suivantes :

  • <mot en français> : À remplacer par :
    • <type> : un type d'OCaml tel qu'int, float, bool, char, string, unit, <fun>, ...
    • <expression> : une expression tel que x + 1.
    • <paramètre> : un nom de paramètre ou une valeur constante tel que x ou 1.
Début de l'exemple
Fin de l'exemple

Les fonctions

modifier

Qu'est-ce qu'une fonction ?

modifier

Les fonctions OCaml sont très proches des fonctions mathématiques. Par exemple
 
s'écrit en OCaml

function x -> x;;

Les fonctions (ou abstractions) sont définies par la syntaxe suivante :


Exemple d'utilisation

modifier

On peut par exemple créer une fonction qui prend un entier en paramètre et renvoie son successeur.

# function x -> x + 1;;
# fun x -> x + 1;; 

- : int -> int = <fun>

Voyons maintenant comment l’utiliser.

# (function x -> x + 1) 10;;

- : int = 11

Définition

modifier

Qu'est-ce qu'une définition ?

modifier

Redéclarer à chaque fois une fonction devient vite pénible. Pour régler le problème, on utilise une définition qui consiste à donner un nom à une valeur. Il faut utiliser le mot-clef let pour assigner un nom à une expression.


On peut aussi effectuer une définition locale avec le mot-clef in:

Les définitions locales sont utiles pour factoriser des calculs répétitifs.

Exemple d'utilisation des définitions

modifier

Pour assigner un entier à un nom.

# let nombre = 10;;

val nombre : int = 10

On peut ensuite récupérer sa valeur à tout moment.

# nombre;;

val nombre : int = 10

Exemple d'utilisation des définitions locales

modifier

Si nous voulons effectuer le calcul suivant :   Ici   est calculé 2 fois. Nous pouvons résoudre ce problème en utilisant les définitions locales.

# let tmp = 2*3 in tmp * 4 + tmp;;
- : int = 30

(* La définition est temporaire. *)
# tmp;;
Error: Unbound value tmp

Définition et fonctions

modifier

Nous pouvons procéder de même pour donner un nom à nos fonctions.


Reprenons l'exemple de la fonction successeur en lui donnant un nom

# let successeur = function x -> x + 1;;

val successeur : int -> int = <fun>
  Il est important de comprendre ici le résultat de l'interpréteur :
val successeur : int -> int = <fun>

Disséquons le élément par élément :

  • val successeur : : 'Successeur' est ici le nom de la définition.
  • int -> int : La fonction prend un entier en paramètre et donne un entier en résultat.
  • = <fun> : La valeur de la définition est une fonction.

Fonctions à plusieurs paramètres

modifier

Une fonction à plusieurs paramètres peut être vue comme une suite de fonctions à un paramètre.

# let fxy = function x -> function y -> x * y;;
(* = *)
# let fxy = fun x y -> x * y;;

val fxy : int -> int -> int = <fun>

Le mot-clef fun permet de soumettre plusieurs paramètres à une fonction contrairement au mot-clef function.

  Il est important de bien comprendre le résultat de la fonction précédente :
[...] int -> int -> int [...]

Il faut savoir que pour le type des fonctions le parenthésage à droite est implicite. Pour plus de compréhension, mettons des parenthèses. on obtient (int -> (int -> int)). On a donc une fonction qui prend en paramètre un entier et qui rend une fonction prenant en paramètre un entier et qui rend un entier. Imaginez que l’on passe à la fonction fxy qu'un seul paramètre : (2 -> (int -> int)) on récupère alors une fonction qui prend un entier en paramètre et retourne un entier en paramètre (int -> int).

# fxy 2;;

- : int -> int = <fun>

Maintenant, imaginez que l’on passe à la fonction fxy deux paramètres : (2 -> (5 -> int)) on récupère un entier.

# ((fxy 2) 5);; 
# (fxy 2) 5;;
# fxy 2 5;;

- : int = 10

Vous remarquez ici que pour l'appel des fonctions le parenthésage à gauche est implicite. C'est très important si vous prenez l'exemple suivant :

# fxy 2 3 + 5;;

- : int = 11

La fonction rend 11 et non pas 16 (2 * 8) OCaml a parenthésé comme ci-dessous.

# ((fxy 2) 3) + 5;;

- : int = 11

Pire si vous mettiez

# fxy 1 + 2 3;;

pour rechercher le produit 3*3. le parenthésage sera effectué comme ci-dessous.

# (((fxy 1) + )2) 3;;

C'est pour cette raison qu'on a droit a une belle erreur de compilation.

Error: This expression has type int -> int
       but an expression was expected of type int

Pour corriger ce problème, il faut mettre des parenthèses sur l'opération.

# fxy (1 + 2) 3;;

- : int = 9

Fonction sans paramètre

modifier

Imaginons maintenant que nous souhaitons une fonction qui renvoie un résultat mais qui n'a pas besoin de paramètre en entrée. Nous pourrions essayer de ne pas spécifier de paramètre comme ci-dessous.

# function -> 1;;

Error: Syntax error

Mais cela ne fonctionne pas.

L'astuce est d’utiliser le type unité.

# let sans_parametre = function () -> 1;;

- : unit -> int = <fun>

Pour appeler la fonction, il suffit de lui passer en paramètre la seule valeur du type unité : ().

# sans_parametre ();;

- : int = 1

Il y a un raccourci d'écriture pour les fonctions.


# let fxy = function x -> function y -> x * y ;;
(* = *)
# let fxy x y = x * y ;;

val fxy : int -> int -> int = <fun>

C'est plus court mais je vous déconseille de l’utiliser tant que vous ne maîtrisez pas encore les fonctions.

Déterminer le type d'une fonction

modifier

Vous avez le nom d'une fonction ? Vous souhaitez connaître le type de ses arguments sans chercher dans la documentation ? Il suffit simplement de marquer le nom de la fonction dans l'interpréteur pour récupérer son type.

# fxy ;;

- : int -> int -> int = <fun>

Et cela marche aussi pour les opérateurs qui peuvent s'utiliser comme des fonctions. Il faut alors utiliser des parenthèses () pour qu’ils soient considérés comme des fonctions.

# (+) ;;

- : int -> int -> int = <fun>

Grâce à ceci, on sait que la fonction (+) peut s'utiliser de façon.

  • Soit avec deux paramètres et la fonction renvoie alors un entier.
# (+) 1 2 ;;

- : int = 3
  • Soit avec un paramètre et la fonction renvoie alors une fonction.
# (+) 1 ;;

- : int -> int = <fun>

Sans vous en rendre compte, vous venez de recoder la fonction successeur.

# let successeur = (+) 1 ;;
val successeur : int -> int = <fun>

# successeur 2 ;;
- : int = 3

Références

modifier

Toutes ces informations sont disponibles sur la documentation officielle :