Précédent Index Suivant

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.








Précédent Index Suivant