La force des ordinateurs est d'être capable de réaliser un très grand nombre de fois des tâches relativement simples. Il peut s'agir de faire un grand nombre de calculs ou encore de traiter une grande quantité de données. Si on doit réaliser une opération sur quelques milliers d'objets de même type, on ne va pas s'amuser à donner un nom différent à chacun d'entre eux. L'idée d'un tableau, array en anglais, est de donner un nom à ce groupe d'objets, puis de numéroter dans un certain ordre tous les objets qui appartiennent à ce groupe.
On a modélisé une référence comme étant une boite simple dans laquelle on pouvait mettre une unique valeur modifiable. Dans le même esprit, un tableau est une grande boite avec plusieurs compartiments. Chaque compartiment, on dira aussi ``case du tableau'', est un peu comme une référence : on peut y mettre une valeur, et la modifier si besoin est.
Une propriété importante à vérifier est que tous les compartiments doivent contenir des objets de même type. On ne fabriquera que plus tard des boites avec des compartiments de natures différentes, pouvant accueillir des objets de types différents.
Une référence sur un entier est de type int ref
, une référence sur un flottant de type float ref
. Selon la même logique le type d'un tableau d'entier est de type int array
, un tableau de flottants est de type float array
, et un tableau de chaîne string array
. En fait, on peut faire des tableaux contenant des valeurs de n'importe quel type.
Le nombre de compartiments contenus dans le tableau est fixé au moment de sa fabrication. On appelle ce nombre la taille, la longueur, ou encore la dimension
du tableau. Il n'est pas possible de changer la taille d'un tableau après sa création.
Chaque compartiment est identifié par un numéro unique. L'élément situé dans le compartiment numéro i et dit ``élément d'indice i''. Attention, les numéros commencent à partir de 0 et non à partir de 1. C'est un peu bizarre au début, mais on s'y habitue très vite. Considérons un tableau contenant taille
compartiments. Le premier élément de ce tableau est donc d'indice 0, le deuxième d'indice 1, le troisième d'indice 2, etc... Enfin, le dernier est d'indice taille-1
(et non pas d'indice taille
puisqu'on ne commence pas à 1).
Suivant la même démarche que pour les références, on va commencer par la création des tableaux, puis on passera à leur manipulation. On mettra alors le tout en pratique sur plusieurs exemples.
De même qu'une boite ne peut pas être vide, les compartiments d'un tableau ne peuvent pas être vides. Pour créer un tableau, il faut donc donner les éléments que l'on veut mettre dedans. Pour construire un tableau, on place entre les symboles [|
et |]
les éléments du tableau, en les séparant par des points-virgules. Par exemple pour construire un tableau avec des compartiments contenant les entiers 4, 9 et 8 :
[| 4; 9; 8 |]
- L'élément à l'indice 0 est 4, celui à l'indice 1 est 9, et celui à l'indice 2 est 8.
- Ce tableau est donc de taille 3, et cette taille est définitive pour ce tableau.
- Il contient des entiers, et ne pourra jamais contenir autre chose que des entiers.
- Le type de ce tableau est ainsi
int array
. - Les valeurs contenues par ce tableau (4, 9 et 8) seront susceptibles d'être modifiées.
Pour nommer ce tableau, il suffit d'utiliser un let
pour y associer un nom :
let mon_tableau = [| 4; 9; 8 |] in ...
On a pris comme premier exemple un tableau d'entiers, mais les tableaux fonctionnent aussi bien avec des valeurs de n'importe quel type. Voici un tableau de flottants, donc de type float array
:
let mon_tableau_float = [| 4.32; 3.49; 9.48; 5.33 |] in ...
On peut également créer un tableau de chaînes, qui sera ainsi de type string array
:
let mon_tableau_string = [| "un"; "super"; "tableau" |] in ...
Cette technique de création ne marche que pour de petits tableaux, puisqu'on ne va pas écrire à la main des milliers de valeurs côtes à côtes. On va donc avoir besoin d'une autre méthode pour construire les tableaux dans des cas plus généraux.
Cette méthode consiste à utiliser une fonction nommée "Array.make"
, traduit mot à mot "Tableau.fabrique"
. Ce nom de fonction est particulier, puisqu'il comporte un point au milieu et commence par une majuscule, alors que les points ne sont normalement pas admis dans les noms de variables, et que ceux-ci doivent ne comporter que des minuscules ou des chiffres.
L'explication est la suivante : Array
correspond à ce qu'on appellera un module. C'est une structure qui permet de regrouper un certain nombre de fonctions. L'intérêt des modules est de mettre un peu d'ordre dans les centaines de noms de fonctions utilisables par le programmeur. On retiendra de cela que toutes les fonctions de manipulations de tableaux ont un nom qui commence par Array
suivit d'un point.
La fonction Array.make
, qui permet de créer un tableau, s'utilise avec deux paramètres. Le premier est la taille du tableau que l'on souhaite créer. Le second paramètre est un objet dont on va mettre une copie dans chacune des cases du tableau que l'on veut créer. C'est une solution simple, qui permet à la fois :
- de préciser le type des compartiments,
- de certifier que tous les compartiments sont de même type,
- de donner un contenu initial à chaque compartiment.
On pourra par la suite modifier le contenu des cases comme on le souhaite.
L'expression " Array.make taille remplissage "
retourne un tableau de longueur taille
, dont toutes les cases contiennent la valeur remplissage
. La longueur d'un tableau est un nombre positif ou nul. La valeur de remplissage peut être de n'importe quel type, et détermine le type du tableau.
Voici par exemple un tableau nommé t
, de taille 5, et rempli de la valeur 8 :
let t = Array.make 5 8 in ...
C'est donc un int array
. Il est équivalent au tableau déclaré élément par élément :
let t = [| 8; 8; 8; 8; 8 |] in ...
Voici maintenant un tableau nommé tab_test
, dont toutes les 25 cases contiennent la chaîne "test"
, et qui est par conséquent de type string array
:
let tab_test = Array.make 25 "test" in
Maintenant que le tableau est créé, voyons comment afficher puis changer ses éléments, c'est-à-dire les valeurs contenues dans ses cases.
Si tab
est un tableau, alors l'expression tab.(i)
représente la valeur contenue dans sa i-ème case. Plus généralement, pour lire une case dans un tableau, on écrit le nom du tableau, puis un point, et enfin entre parenthèses l'indice de cette case, le tout sans espaces.
Par exemple, après la déclaration suivante de mon_tableau
let mon_tableau = [| 4; 9; 8 |] in ...
l'expression mon_tableau.(0)
vaut 4, mon_tableau.(1)
vaut 9, et mon_tableau.(2)
vaut 8. On peut faire afficher cela par le programme :
let mon_tableau = [| 4; 9; 8 |] in print_int mon_tableau.(0); print_newline(); print_int mon_tableau.(1); print_newline(); print_int mon_tableau.(2);
On verra un peu plus tard comment récupérer la taille d'un tableau et utiliser une boucle pour afficher tous les éléments d'un tableau, sans recopier ainsi le code.
La lecture fonctionne exactement de la même façon avec les tableaux déclarés à l'aide de Array.make
. Dans l'exemple suivant, le tableau tab_test
contient la chaîne "test"
dans toutes ses cases. On en affiche quelques-unes à l'aide d'un print_string
pour voir :
let tab_test = Array.make 25 "test" in print_string tab_test.(0); print_newline(); print_string tab_test.(9); print_newline(); print_string tab_test.(24);