 
 
 
 Calculatrice à mémoire
On reprend maintenant l'exemple de la calculatrice décrite dans le
chapitre précédent mais en la dotant cette fois d'une interface
utilisateur rendant notre programme propre à être utilisé comme
une calculette de bureau. Cette boucle permet d'entrer les
opérations directement et de voir s'afficher les résultats sans
avoir à appliquer explicitement la fonction de transition pour chaque
pression d'une touche.
Nous ajoutons quatre nouvelles touches : C qui remet à 0
l'affichage, M qui met en mémoire un résultat, m qui
restitue cette mémoire et OFF qui << éteint >> la
calculatrice. Ceci correspond au type suivant :
# type touche = Plus | Moins | Fois | Par | Egal | Chiffre of int 
             | MemoireIn | MemoireOut | Clear | Off ;;
Il faut alors définir une fonction de traduction des caractères
frappés au clavier en des valeurs de type touche. L'exception
Touche_non_valide permet de traiter le cas des caractères
ne représentant pas une touche de la calculette. La fonction
code du module Char traduit un caractère en son
code ASCII.
# exception Touche_non_valide ;;
exception Touche_non_valide
# let traduction c = match c with 
     '+' -> Plus
   | '-' -> Moins
   | '*' -> Fois
   | '/' -> Par
   | '=' -> Egal
   | 'C' | 'c' -> Clear
   | 'M' -> MemoireIn
   | 'm' -> MemoireOut
   | 'o' | 'O' -> Off
   | '0'..'9' as c -> Chiffre ((Char.code c) - (Char.code '0'))
   | _ -> raise Touche_non_valide ;;
val traduction : char -> touche = <fun>
Dans un style impératif, la fonction de transition ne calculera plus
un nouvel état, mais modifiera physiquement l'état de la
calculette. Il faut donc redéfinir le type etat de façon
à ce que ses champs soient modifiables. Enfin, on définit
l'exception Touche_off pour traiter l'activation de la touche
OFF.
# type etat = {
  mutable dce : int;     (* dernier calcul effectué      *)
  mutable dta : bool;    (* vrai si touche = chiffre     *)
  mutable doa : touche;  (* dernier opérateur actionné   *)
  mutable vaf : int;     (* valeur affichée              *)
  mutable mem : int      (* mémoire de la calculette     *) };;
# exception Touche_off ;;
exception Touche_off
# let transition et tou = match tou with 
     Clear -> et.vaf <- 0 
   | Chiffre n  ->  et.vaf <- ( if et.dta then et.vaf*10+n else n );
                    et.dta <- true
   | MemoireIn  ->  et.dta <- false ; 
                    et.mem <- et.vaf 
   | MemoireOut ->  et.dta <- false ;
                    et.vaf <- et.mem
   | Off -> raise Touche_off
   |  _  -> let dce = match et.doa with  
                         Plus  -> et.dce + et.vaf  
                       | Moins -> et.dce - et.vaf 
                       | Fois  -> et.dce * et.vaf 
                       | Par   -> et.dce / et.vaf 
                       | Egal  -> et.vaf
                       | _ -> failwith "transition: filtre impossible"
                   in 
              et.dce <- dce ;
              et.dta <- false ; 
              et.doa <- tou ;
              et.vaf <- et.dce;;
val transition : etat -> touche -> unit = <fun>
On définit la fonction go qui lance la calculette.
Sa valeur de retour est () puisque ne nous importe que l'effet
produit par l'exécution sur l'environnement (entrée/sortie,
modification de l'état). Son argument est également la constante
() puisque la calculette est autonome (elle définit elle-même
son état initial) et interactive (les données du calcul 
sont rentrées au clavier au fur et à mesure des besoins). Les
transitions s'effectuent dans une boucle infinie (while
true do) dont on pourra sortir par l'exception
Touche_off. 
# let go () = 
   let etat = { dce=0; dta=false; doa=Egal; vaf=0; mem=0 } 
   in try 
        while true do 
          try 
            let entree = traduction (input_char stdin)
            in transition etat entree ;
               print_newline () ;
               print_string "affichage : " ;
               print_int etat.vaf ;
               print_newline () 
          with
            Touche_non_valide -> ()  (* pas d'effet *)
        done
      with
        Touche_off -> () ;;
val go : unit -> unit = <fun>
Notons que l'état initial doit être soit passé en paramètre, soit
déclaré localement à l'intérieur de la fonction go pour
qu'il soit initialisé à chaque application de cette fonction. Si
nous avions utilisé une valeur etat_initial comme dans le
programme fonctionnel, la calculatrice repartirait dans le même
état que celui qu'elle avait lorsqu'elle a été coupée. Il
aurait été alors difficile d'utiliser deux calculatrices dans le même
programme.
 
 
 
