Premiers pas en OCaml/Fonctions

Leçons de niveau 14
Une page de Wikiversité, la communauté pédagogique libre.
Début de la boite de navigation du chapitre
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.

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.

Convention de nommage[modifier | modifier le wikicode]

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 | modifier le wikicode]

Qu'est-ce qu'une fonction ?[modifier | modifier le wikicode]

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 | modifier le wikicode]

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 | modifier le wikicode]

Qu'est-ce qu'une définition ?[modifier | modifier le wikicode]

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 | modifier le wikicode]

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 | modifier le wikicode]

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 | modifier le wikicode]

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>
Panneau d’avertissement 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 | modifier le wikicode]

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.

Panneau d’avertissement 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 | modifier le wikicode]

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

Sucre syntaxique[modifier | modifier le wikicode]

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 | modifier le wikicode]

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 | modifier le wikicode]

Toutes ces informations sont disponibles sur la documentation officielle :