Diaporama n°11¶

Le langage OCaml - Premiers exemples¶

Avec le toplevel¶

exécuter ocaml

image.png

Avec utop¶

exécuter utop (Universal TOP-level)

image-2.png

Types en OCaml¶

les types de base usuels:¶

  • bool : true et false

  • int : entiers signés sur 31 bits (ou 63 bits)

  • float : réels (sur 64 bits)

  • char : caractère ASCII

Types en OCaml¶

  • le type unit : () (rien)

  • les types construits prédéfinis (chaînes de caractères, n-uplets, listes chaînées, tableaux) ou à définir (cf plus tard)

  • les types fonction : T -> T'

NB : le type des expressions est vérifié avant que les calculs soient exécutés

Les opérateurs numériques¶

Les opérateurs numériques pour float et int sont différents.

  • +, -, *, /, mod pour les int

  • +., -., *., /., ** pour les float

NB : il n'y a pas de conversions implicites en OCaml

In [ ]:
2 + 2;;
In [42]:
2 + 2;;
Out[42]:
- : int = 4

- : int = 4 signifie : l'expression calculée est de type int et vaut 4

Plus généralement :

nom : type = valeur . Ici, on n'a pas défini de nom particulier ...

In [ ]:
2.0 + 2.0;;
In [39]:
2.0 + 2.0;;
File "[39]", line 1, characters 0-3:
1 | 2.0 + 2.0;;
    ^^^
Error: This expression has type float but an expression was expected of type
         int

Erreur : l'opérateur + attend de opérandes de type int

In [ ]:
2.0 +. 2.0;;
In [40]:
2.0 +. 2.0;;
Out[40]:
- : float = 4.

Les opérations de conversions se font avec des appels fonctions

NB : programmation fonctionnelle : on dit plutôt appliquer une fonction

In [ ]:
int_of_float 2.0 + 2;;
In [43]:
int_of_float 2.0 + 2;;
Out[43]:
- : int = 4

la fonction int_of_float : convertit un float en int (on récupère un int à partir (of) d'un float)

NB : l'application de fonction se fait sans parenthèses

NB : l'application de fonction est prioritaire par rapport aux opérations numériques.

Dans le doute, on écrirait (int_of_float 2.0) + 2

Exceptions¶

Le langage OCaml dispose d'un mécanisme d'exceptions (comme Python)

In [44]:
3/0;;
Exception: Division_by_zero.
Raised by primitive operation at unknown location
Called from file "toplevel/toploop.ml", line 208, characters 17-27

L'expression est correctement typée (division entière avec deux opérandes entiers)

C'est à l'éxécution que le calcul s'interrompt

Les opérateurs de comparaison¶

  • ATTENTION : =, <> : égalité, inégalité

  • <, >, <=, >= : ...

NB : ce sont les mêmes opérateurs pour les types de base ...

NB : ... mais on ne peut pas comparer expressions de types différents

In [ ]:
2 < 3;;
In [52]:
2 < 3;;
Out[52]:
- : bool = true
In [ ]:
2.0 < 3.0;;
In [53]:
2.0 < 3.0;;
Out[53]:
- : bool = true
In [ ]:
2.0 < 3;;
In [54]:
2.0 < 3;;
File "[54]", line 1, characters 6-7:
1 | 2.0 < 3;;
          ^
Error: This expression has type int but an expression was expected of type
         float

Les opérateurs booléens¶

  • ATTENTION : not, &&, ||

NB : évaluation paresseuse (comme en Python, en C ...)

Exemple 1 : variable globale¶

In [ ]:
(* Ceci est un commentaire *)

let x = "Hello world !";;
In [88]:
(* Ceci est un commentaire *)

let x = "Hello world !";;
Out[88]:
val x : string = "Hello world !"
  • un commentaire (* ...*)

  • let signifie "soit" au sens mathématique

  • let x = expr;; définition de variable globale x initialisée à expr

  • ;; termine une phrase OCaml

On a défini une variable nommée x, de type string qui vaut "Hello world !"

NB : string le type chaîne de caractère

NB : le type a été inféré (= déduit) par le système (ici, c'était facile ...)

In [ ]:
x;;
In [89]:
x;;
Out[89]:
- : string = "Hello world !"
  • x est bien un variable globale : elle est toujours accessible

  • - : signifie qu'on n'a pas défini de nouvelle variable

  • cette expression est de type int et vaut "Hello world !"

En OCaml, une variable est toujours initialisée

In [ ]:
let z;;
In [19]:
let z;; (* CODE FAUX : il manque la valeur ...*)
File "[19]", line 1, characters 5-7:
1 | let z;;
         ^^
Error: Syntax error

En OCaml, une variable n'est pas modifiable (sauf ...) : immuable ou non-mutable

NB: quelques cas particuliers (cf OCaml impératif)

NB: ça change totalement la façon de programmer ! Et les contre-sens sont fréquents chez les débutants

In [ ]:
x = "Bonjour le monde !";;
In [90]:
x = "Bonjour le monde !";;
Out[90]:
- : bool = false

ATTENTION : = est l'opérateur de comparaison

Cette expression est de type bool et est fausse (ce sont deux chaînes de caractères différentes)

Exemple trompeur¶

on peut redéfinir une variable de même nom

In [ ]:
let x = 1;;

let x = x + 1;;

x;;
In [57]:
let x = 1;;

let x = x + 1;;

x;;
Out[57]:
val x : int = 1
Out[57]:
val x : int = 2
Out[57]:
- : int = 2
  • x est une nouvelle variable initialisé avec la valeur de l'ancienne augmentée de 1
  • l'ancienne variable est inaccessible

Exemple 2 : variable locale¶

In [ ]:
let z = 3 in z * z;;
In [1]:
let z = 3 in z * z;;
Out[1]:
- : int = 9
  • let z = expr1 in expr2 ;; définition de la variable locale z, initialisée avec expr1, puis utilisée pour calculer expr2

  • la variable de z n'est utilisable que pour calculer expr2 (portée locale)

  • le résultat n'a pas de nom, c'est un entier (int) qui vaut 9

NB : là encore, le type a été inféré (un peu moins facile)

In [ ]:
z;;
In [30]:
z;;
File "[30]", line 1, characters 0-1:
1 | z;;
    ^
Error: Unbound value z
  • z était bien une variable locale. Elle n'est plus accessible

Une variable locale peut venir "éclipser" une variable globale

In [ ]:
let x = 3;; (* x : variable globale *)
let x = 5 in x + 1;; (* x: variable locale*)
x;; (* c'est la variable globale *)
In [2]:
let x = 3;; (* x : variable globale *)
let x = 5 in x + 1;; (* x: variable locale*)
x;; (* c'est la variable globale *)
Out[2]:
val x : int = 3
Out[2]:
- : int = 6
Out[2]:
- : int = 3

Exemple 3 : fonction d'une variable¶

In [ ]:
let f x = x * x;;
In [32]:
let f x = x * x;;
Out[32]:
val f : int -> int = <fun>
  • let f x = expr définit une fonction f de paramètre x qui calcule

  • on a défini une variable globale f de type int -> int qui vaut ... une fonction

  • NB : type int -> int inféré par OCaml !

  • ATTENTION : pas de parenthèse autour des paramètres

  • ATTENTION : pas de return

ATTENTION :¶

il n'y a pas de return en OCaml¶

Appel de fonction (application)¶

In [ ]:
f 4;;
In [33]:
f 4;;
Out[33]:
- : int = 16

ATTENTION : un appel de fonction en OCaml se fait sans parenthèses

Une fonction a un type¶

In [ ]:
f;;
In [58]:
f;;
Out[58]:
- : int -> int = <fun>
In [ ]:
int_of_float;;
In [59]:
int_of_float;;
Out[59]:
- : float -> int = <fun>

Certaines fonctions ne "calculent" rien¶

In [60]:
print_int;;
Out[60]:
- : int -> unit = <fun>
  • print_int affiche un int à l'écran ("effet")
In [62]:
print_int 5;;
Out[62]:
- : unit = ()

La valeur renvoyée est () (rien) de type unit

Fonctions locales¶

In [ ]:
let f x = x * x in f 3 + f 4 = f 5
In [63]:
let f x = x * x in f 3 + f 4 = f 5
Out[63]:
- : bool = true
  • la fonction locale f qui calcule le carré d'un nombre est appliquée 3 fois

  • l'expression f 3 + f 4 = f 5 (égalité entre 9 + 16 et 25 ?) s'évalue en true

Inférence de type¶

Comment OCaml déduit-il le type de f ?

In [64]:
let f x = x * x;;
Out[64]:
val f : int -> int = <fun>
  • * est la multiplication entière
  • donc x doit être entier (type du paramètre)
  • donc x * x est entier (type de la valeur calculée)

Donc f : int -> int

Quel est le type de la fonction id ?

In [ ]:
let id x = x;;
In [65]:
let id x = x;;
Out[65]:
val id : 'a -> 'a = <fun>

Polymorphisme : cette fonction peut s'appliquer à différents types de valeur

'a est appelé variable de type, il représente un type quelconque

In [73]:
id 42;;
id 1.3;;
id "Hello World !";;
Out[73]:
- : int = 42
Out[73]:
- : float = 1.3
Out[73]:
- : string = "Hello World !"
In [74]:
id ();;
Out[74]:
- : unit = ()

Exemple 4 : fonctions de deux variables¶

In [75]:
let sum x y = x + y;;
Out[75]:
val sum : int -> int -> int = <fun>

À lire ainsi :

  • les deux premiers int sont les types des paramètres,
  • le dernier intest le type du résultat

NB : sum est une variable globale

NB : on déduit son type car + opére sur des entiers

Application (sans parenthèses)¶

In [76]:
sum 5 4;;
Out[76]:
- : int = 9

Attention aux priorités :

  • l'application est prioritaire sur les opérateurs numériques
  • dans le doute, parenthéser
In [ ]:
sum 4 1 * 3;;
In [77]:
sum 4 1 * 3;;
Out[77]:
- : int = 15
In [ ]:
sum 4 (1 * 3);;
In [78]:
sum 4 (1 * 3);;
Out[78]:
- : int = 7
In [ ]:
sum (4 + 1) 3;;
In [79]:
sum (4 + 1) 3;;
Out[79]:
- : int = 8

Les opérateurs "infixes"¶

NB : infixe qui se place au milieu (préfixe:avant, postfixe:après)

Ce sont des fonctions de deux variables.

HP : utiliser l'opérateur noté entre parenthèses pour l'utiliser comme une fonction

In [81]:
(+);;
Out[81]:
- : int -> int -> int = <fun>
In [83]:
(+) 4 5;;
Out[83]:
- : int = 9
In [85]:
(<);;
Out[85]:
- : 'a -> 'a -> bool = <fun>

Polymorphe !

In [86]:
(=);;
Out[86]:
- : 'a -> 'a -> bool = <fun>

Exemple 5 : fonctions de n variables¶

  • let f arg1 arg2 ... argn = expr : fonction globale

  • let f arg1 arg2 ... argn = expr1 in expr2 : fonction locale

Exemple 6 : utilisation des modules¶

OCaml dispose de nombreux modules qui contiennent de nombreuses variables et fonctions.

Par exemple : le module Random contient la fonction float

Random.float x renvoie un nombre aléatoire entre 0 et x

convention OCaml :

  • un nom de module commence par une majuscule
  • un nom de variable ne doit pas commencer par une majuscule
In [100]:
Random.float;;
Out[100]:
- : float -> float = <fun>
In [96]:
Random.float 1.0;;
Out[96]:
- : float = 0.703106837148469
In [109]:
open Random;; (* permet d'utiliser directement les fonctions du module *)

float 1.0;;
Out[109]:
- : float = 0.122128067547237706

NB : comme from random import * en Python

Exemple 7 : fonctions de 0 variable¶

Comment définir une fonction sans paramètre ? par exemple la fonction random qui renvoie en float entre 0 et 1 ?

let random = expr ?

INCORRECT : c'est la définition d'une variable

  • expr est évalué tout de suite

CORRECT let random () = expr

  • expr n'est évalué qu'au moment de l'appel random ()
In [101]:
let random = Random.float 1.0;; (* INCORRECT *)
Out[101]:
val random : float = 0.918342174320491
In [102]:
random;; (* Pas très aléatoire ce nombre ...*)
Out[102]:
- : float = 0.918342174320491
In [104]:
random ();; (* INCORRECT *)
File "[104]", line 1, characters 0-6:
1 | random ();; (* INCORRECT *)
    ^^^^^^
Error: This expression has type float
       This is not a function; it cannot be applied.
In [106]:
let random () = Random.float 1.0;;
Out[106]:
val random : unit -> float = <fun>
In [107]:
random ();;
Out[107]:
- : float = 0.791841585710404638
In [108]:
random ();;
Out[108]:
- : float = 0.932074421228699213

Exercices¶

Exercice 1¶

Définir deux fonctions globales :

  • carre:int->int

  • cube:int->int

Est-ce que $2^3$ et $3^2$ sont égaux ?

Exercice 2¶

La fonction racine carrée fait-elle partie de la StdLib ? Quel est son type ?

Ecrire une fonction dist:float->float->float telle que dist x y calcule la distace OM avec $O(0,0)$ et $M(x,y)$

Exercice 3¶

L'appel Random.float 1.0 renvoie un float aléatoire compris entre 0 et 1 (probabilité uniforme)

Ecrire une fonction boolAlea:unit->bool telle que boolAlea () renvoie true avec une probabilité 0.5 et false avec une probabilité 0.5

A Suivre ...¶