Cher Hugo,

Je te propose une petite utilisation amusante du langage Objective Caml : le codage et décodage de messages.

Les messages seront présentés sous forme de chaînes  de caractères. Par exemple :
# "LA CIMAISE ET LA FRACTION" ;;
- : string = "LA CIMAISE ET LA FRACTION"
Je n'utilise que des lettres majuscules sans accent pour simplifier.

Comme il nous servira par la suite, nous allons donner le nom message à  ce message :
# let message = "LA CIMAISE ET LA FRACTION" ;;
val message : string = "LA CIMAISE ET LA FRACTION"
On applique à  message une mystérieuse fonction code et on obtient :
# code message ;;
TI KQUIQAM MB TI NZIKBQWV
- : unit = ()
Attention, notre fonction modifie la valeur du message qu'on lui donne :
# message ;;
- : string = "TI KQUIQAM MB TI NZIKBQWV"
On a, bien sûr, la fonction inverse (decode) qui permet de retrouver le message original :
# decode message ;;
LA CIMAISE ET LA FRACTION
- : unit = ()
Tout le travail maintenant est d'écrire les fonctions code et decode. Mais avant d'en arriver là, voici quelques mots sur le principe de codage utilisé.

Principes du codage par substitution
Outre l'espace qui sépare les mots, que l'on a laissé  intact, le message est uniquement composé  des lettres de l'alphabet latin majuscule. Pour obtenir le message codé, on a simplement remplacé  chacune des lettres du message de départ par une autre (d'oùle terme << codage par substitution >>)

L'association entre lettres du message de départ et lettres du message codé  que l'on a utilisée  est la suivante :
  A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
  I J K L M N O P Q R S T U V W X Y Z A B C D E F G H
On a donc remplacé  le A par I, le B par J, ..., le R par Z, le S par A, etc.

Remplacement d'un caractère par un autre dans une chaîne  de caractères
La première opération qu'il faut savoir effectuer est de remplacer une lettre par une autre dans un message.

Je te rappelle que les << messages >> dont nous nous occupons sont, du point de vue du langage de programmation, des chaînes  de caractères : ce que l'on prononce string en Objective Caml. Et les lettres sont appelées de façon plus générale des caractères : ce que l'on prononce char en Objective Caml. Les caractères sont écrit entre apostrophes :
# 'A' ;;
- : char = 'A'
Dans une chaîne, les caractères sont rangés dans un certain ordre, ce qui fait que l'on peut parler du 1ier caractère, du 2ième, ..., du i-ième, etc. En d'autres termes : chaque caractère présent dans une chaîne  de caractère y possède un numéro ; en informatique, on dit un indice. Curieusement, mais il y a de bonnes raisons pour cela, les numéros des caractères commencent à  0 et non pas à  1. Ainsi, le premier caractère de message a le numéro 0 et on peut y accéder en écrivant :
# message.[0] ;;
- : char = 'L'
Le deuxième caractère a le numéro 1 :
# message.[1] ;;
- : char = 'A'
etc. jusqu'au dernier, qui a le numèro 24 :
# message.[24] ;;
- : char = 'N'
Si l'on essaie d'aller au-delà  du dernier caractère, on obtient une erreur d'exécution :
# message.[25];;
Uncaught exception: Invalid_argument("String.get")
On peut modifier la valeur d'un caractère d'une chaîne  en écrivant :
# message.[2] <- 'X' ;;
- : unit = ()
Pour voir le résultat obtenu, on peut utiliser la fonction d'affichage à  l'écran des chaînes  de caratères :
# print_string message ;;   
LAXCIMAISE ET LA FRACTION- : unit = ()
On a ici remplacé  l'espace entre LA et CIMAISE par le caractère 'X'.

Parcours des caractères d'une chaîne
Si on veut répéter l'opération ci-dessus de remplacement des espaces, par un X, on peut écrire un petit programme qui examinera chacun des caractères de message et leur substituera un X chaque fois qu'elle trouvera un espace.

Pour savoir si un caractère de message est égal à  l'espace ou non, on utilise un test d'égalité. La valeur de ce test est true (i.e vrai) ou false (i.e. faux). Par exemple :
# message.[2] = ' ' ;;
- : bool = false
# message.[10] = ' ' ;;
- : bool = true
Si l'on veut effectuer une action lorsque qu'un test est vrai, on utilise une instruction des langages de programmation appelée conditionnelle et que l'on écrit si ... alors ... en anglais. Par exemple on va tester ici l'égalité  du caratère numéro 10 avec l'espace et si ce test est vrai, on fera deux choses : remplacer l'espace par un X puis afficher le nouveau contenu de message.
# if  message.[10] = ' ' then 
   (message.[10] <- 'X'; print_string message) ;;
LAXCIMAISEXET LA FRACTION- : unit = ()
On a mis entre parenthèses les deux actions à  effectuer et on les a séparées par un point-virgule.

Si le caractère testé  n'est pas un espace, l'action associée aprés le then n'est pas effectuée :
# if  message.[10] = ' ' then 
   (message.[10] <- 'X'; print_string message) ;;  
- : unit = ()
Ici donc, message n'est pas réaffiché  et il n'a pas étémodifé. Ce que l'on peut vérifier en demandant sa valeur :
# message ;;
- : string = "LAXCIMAISEXET LA FRACTION"
Pour examiner chacun des caractères de message, on utilise une instruction particulière des langages de programmation appelée boucle. Une boucle est destinée à  répéter un certain nombre de fois une action. L'action que nous allons ici répéter est l'instruction conditionnelle suivante
if message.[i] = ' ' then message.[i] <- 'X'
Remarque que le numéro de caractère testé  n'est pas donné  par un nombre, mais par une variable i. C'est la structure d'instruction de boucle qui donne à  cette variable toutes les valeurs comprise entre une première (on prendra ici 0) et une dernière (on prendra ici, 24 qui le numéro du dernier caractère de message). Aprés la boucle, on affichera la valeur modifée de message.

Voici ce petit programme :
let message = "LA CIMAISE ET LA FRACTION" ;;
for i=0 to 24 do 
 if message.[i] = ' ' then message.[i] <- 'X'
done;
print_string message ;;
LAXCIMAISEXETXLAXFRACTION- : unit = ()
Je te laisse faire en exercice le petit programme qui fait le travail inverse : remplacer les X par des espaces.

Listes d'association
Pour obtenir le programme de codage des messages, il faut faire connaître à  la machine l'aasociation entre lettres de l'alphabet utilisée. Comme à  chacune des lettre correspond une autre lettre, on utilisera, pour chaque lettre, ce que l'on appelle une structure de couple. Un couple contient deux élément séparés par une virgule et l'on peut mettre le tout entre parenthèses. Ainsi, l'association entre A et I s'écrit :
# ('A','I') ;;
- : char * char = 'A', 'I'
Pour obtenir toutes les associations, on écrit un couple pour chaque lettre et on regroupe tous ces couples dans une structure de liste que l'on appelera clef :
# let clef =
   [ ('A', 'I') ; ('B', 'J') ; ('C', 'K') ; ('D', 'L') ; ('E', 'M') ;
     ('F', 'N') ; ('G', 'O') ; ('H', 'P') ; ('I', 'Q') ; ('J', 'R') ;
     ('K', 'S') ; ('L', 'T') ; ('M', 'U') ; ('N', 'V') ; ('O', 'W') ;
     ('P', 'X') ; ('Q', 'Y') ; ('R', 'Z') ; ('S', 'A') ; ('T', 'B') ;
     ('U', 'C') ; ('V', 'D') ; ('W', 'E') ; ('X', 'F') ; ('Y', 'G') ;
     ('Z', 'H') ] ;;
val clef : (char * char) list =
  ['A', 'I'; 'B', 'J'; 'C', 'K'; 'D', 'L'; 'E', 'M'; 'F', 'N'; 'G', 'O';
   'H', 'P'; 'I', 'Q'; 'J', 'R'; 'K', 'S'; 'L', 'T'; 'M', 'U'; 'N', 'V';
   'O', 'W'; 'P', 'X'; 'Q', 'Y'; 'R', 'Z'; 'S', 'A'; 'T', 'B'; 'U', 'C';
   'V', 'D'; 'W', 'E'; 'X', 'F'; 'Y', 'G'; 'Z', 'H']
Remarque que la réponse du système est plus paresseuse que le programmeur puisqu'elle ne contient plus les parenthèses.

Le problème maintenant est de savoir trouver quelle lettre il faut substituer à  une lettre du message en clair. Pour cela, le langage Objective Camlfournit une fonction prédéfinie appelée List.assoc Ce nom en deux partie vient du fait que les fonctions sont regroupées par thèmes dans des fichiers appelés modules. Ainsi, il existe un module donnant des fonctions sur les listes et il s'appelle List. La fonction de ce module qui nous interresse s'appelle assoc, pour <<association>>. On utilise cette fonction en lui donnant en premier argument la lettre dont on veut la correspondante et en second argument la liste des associations. Par exemple :
# List.assoc 'L' clef ;;
- : char = 'T'
Pour obtenir le codage d'une lettre de message, on utilise l'écriture permettant d'accéder aux caratères d'une chaîne en premier argument :
# List.assoc message.[0] clef;;
- : char = 'T'
On a obtenu 'T' car le caractère numéro 0 de message est 'L'.

Le programme de codage
On a maintenant tout ce qu'il faut pour écrire le programme de codage. On a la boucle pour examiner et modifier chacune des lettres du message, la conditionnelle pour savoir si la lettre examinée est un espace ou non (ici on ne veut pas remplacer les espaces, on utilise le test de différence noté  <>) et les listes d'association pour obtenir le code de chaque lettre.
# let message = "LA CIMAISE ET LA FRACTION" ;;        
# for i=0 to 24 do
   if message.[i] <> ' ' then 
     message.[i] <- List.assoc message.[i] clef
   done ;
  print_string message;
  print_newline() ;;
TI KQUIQAM MB TI NZIKBQWV
- : unit = ()
Programmes et fonctions
On voudrait pouvoir utiliser le programme ci-dessus pour coder autre chose que le message LA CIMAISE ET LA FRACTION, par exemple, LA FRACTION N EST PAS PREVISIBLE. Une solution simple est de remplacer la première ligne du programme et de garder le reste. Ça donne ;
# let message = "LA FRACTION N EST PAS PREVISIBLE" ;;        
# for i=0 to 24 do
   if message.[i] <> ' ' then 
     message.[i] <- List.assoc message.[i] clef
   done ;
  print_string message;
  print_newline() ;;
TI NZIKBQWV V MAB XIA XZMVISIBLE
- : unit = ()
Le résultat obtenu n'est pas tout à  fait celui escompté : les sept derniers caratères de message n'ont pas été  codés.

En effet, notre second message contient 31 caractères alors que le premier en contenait 24. On pourrait remplacer 24 par 31 dans le programme, mais l'on peut faire mieux. La boucle de codage commence toujours au premier caractère et fini toujours au dernier. Si, étant donnée une chaîne  de caractères, on sait calculer le numéro du dernier caractère, on pourra écrire une boucle qu'il n'y aura pas besoin de modifier quand on change la valeur de message.

Pour effectuer ce calcul, on fait appel à  la fonction String.length qui nous donne le nombre de caractères que contient une chaîne  (on dit qu'elle donne la longueur de la chaîne). Attention, comme la numérotation commence à  0, le numéro du dernier caractère est égal au nombre de caratères moins un ! On obtient le programme suivant :
# let message = "LA FRACTION N EST PAS PREVISIBLE" ;;        
# for i=0 to (String.length message) - 1 do
   if message.[i] <> ' ' then 
     message.[i] <- List.assoc message.[i] clef
   done ;
  print_string message;
  print_newline() ;;
TI NZIKBQWV V MAB XIA XZMDQAQJTM
- : unit = ()
La programmation bien comprise est une activité  pour fainéants : on ne veut pas écrire trente six fois la même chose. Et les langages de programmation sont faits pour aller dans le sens de cette qualité  (la fainéantise :) en fournissant au programmeur la possibilité  de définir des fonctions. Ici, la seule chose qui changera lorsque l'on voudra coder un nouveau message, sera la valeur dudit message. Autrement dit, message est un paramètre du programme de codage. Et on peut définir une fonction qui prendra en paramètre (ou en argument) un message (c'est à  dire une chaîne  de caractères). Voici comment :
# let code message = 
   for i=0 to (String.length message) - 1 do
     if message.[i] <> ' ' then 
       message.[i] <- List.assoc message.[i] clef
     done ;
    print_string message;
    print_newline() ;;
val code : string -> unit = <fun>
Pour obtenir le code d'un autre message, il suffit d'appliquer la fonction ainsi définie à  ce message :
# code "EH BIEN DEBAGOULEZ MAINTENANT" ;;
MP JQMV LMJIOWCTMH UIQVBMVIVB
- : unit = ()
Exercice
Comment écrire une fonction de décodage decode ? Peut-on faire en sorte que la même fonction puisse servir à  la fois au codage et au décodage ?

Bien à  toi, Pascal.

PS: j'utilise Objective Caml  sous un système unix, c'est pourquoi mes exemples se présentent sous la forme :
# "phrase tapée par le programmeur" ;;
- : type = réponse du système commençant par - ou un nom
Sous Windows, lorsque tu lances Objective Caml, tu obtiens trois fenêtres. Les <<phrases tapées par le programmeur>> (qui sont précédées du signe # et terminées par le double point virgule ;;) doivent l'être (tapées) dans la fenêtre d'entrée en bas à  gauche et la << réponse du système >> apparaîtra dans la fenêtre de gauche au-dessus.
This document was translated from LATEX by HEVEA.