td3 Polytech : un dérivateur formel Ocaml en 5 minutes

On pourra comparer cette version OCAML avec la version HASKELL voire la version Python .

OCaml
(* Le but est d'ajouter des constantes symboliques comme Pi.  *)
 
(* Il faut bien choisir comment étendre la structure de données de *)
(* manière avoir un joli code derrière.  *)
 
(* Pour l'exercice on ajoute juste pi et e, mais il faut imaginer que *)
(* dans le futur on en ajoutera d'autres.  *)
 
(* Solution pas viable : *)
 
type expr0 =
 | Num of float
 | Var of string
 | Add of expr0 * expr0
 | Symb of string
 
(* Pas bon car pas d'aide de la part du système de types sur les *)
(* erreurs potentielles avec les string.  *)
 
 
 
 
     
(* Assoc pour l'environnement *)
type envir = (string*float) list
 
let rec assoc s (env : envir) =
  match env with
  | []       -> failwith "Variable inconnue"
  | (x,f)::r -> if x = s then f else assoc s r
 
 
      
(* 1ere solution *)
 
type expr1 =
 | Num of float
 | Var of string
 | Add of expr1 * expr1
 | Pi
 | E
 
(* Pas de difficulté pour coder eval. *)
 
let rec eval env e = match e with
  | Num f -> f
  | Var s -> assoc s env
  | Add (e1,e2) -> (eval env e1) +. (eval env e2)
  | Pi -> 3.14
  | E -> 2.7
 
(* Attention! Comment associer *)
(* une valeur  numérique aux valeurs symboliques.  *)
 
(* Problème de cette solution : ici on ne le voit pas très clairement, *)
(* mais il faut  se projeter sur les autres fonctions,   en *)
(* particulier la dérivation. Toutes les constantes symboliques se *)
(* dérivent  de la même manière. Et pour éval aussi,  toutes les *)
(* constantes se manipulent de la même manière : on renvoit un *)
(* nombre. Donc il faudrait factoriser ce traitement, et pour cela *)
(* avoir un constructeur dédié dans la structure de   données -> *)
(* solution 2.   *) 
 
(* Solution 2. *)
 
type symbol = Pi | E ;;
 
type expr =
 | Num of float
 | Var of string
 | Add of expr * expr
 | Mul of expr * expr
 | Cos of expr
 | Sin of expr
 | SConst of symbol
 
 
let value_of s = match s with
    | Pi -> 3.14
    | E -> 2.7
 
let rec eval2 env e = match e with
  | Num f -> f
  | Var s -> assoc s env
  | Add (e1,e2) -> (eval2 env e1) +. (eval2 env e2)
  | SConst v -> value_of v
 
 
     
(* Problème : value_of est un peu rigide. Solution : mettre cette *)
(* fonction en paramètre.  *)
 
(* Solution 2 bis (même structure de données que la solution 2) :  *)
 
let rec eval env values e = match e with
  | Num f -> f
  | Var s -> assoc s env
  | Add (e1,e2) -> (eval env values e1) +. (eval env values e2)
  | Mul (e1,e2) -> (eval env values e1) *. (eval env values e2)
  | Cos e -> cos (eval env values e)
  | Sin e -> sin (eval env values e)
  | SConst v -> values v
 
(* Exemple d'utilisation : *)
 
let val_1 s = match s with
    | Pi -> 3.1
    | E -> 2.7
 
let val_2 s = match s with
    | Pi -> 3.14
    | E -> 2.71
 
let exemple = Mul(Cos (SConst Pi), Add (Var "x", Add(Var "y", Num 1.)))
     
let env1 = [ ("x",2.) ; ("y",10.) ]
 
let ex_eval = eval env1 val_2 exemple
 
  
let rec deriv var = function
  | Num f        -> Num 0.
  | Var s        -> if s = var then Num 1. else Num 0.
  | Add (e1, e2) -> Add (deriv var e1, deriv var e2)
  | Mul (e1, e2) -> Add (Mul (e1, deriv var e2), Mul (e2, deriv var e1))
  | Cos e        -> Mul (Mul (Num (-1.), deriv var e ), Sin e) 
  | Sin e        -> Mul (deriv var e, Cos e)
  | SConst v     -> Num 0. 
 
let ex_eval_der = eval env1 val_2 (deriv "x" exemple)
     
  
(* ou si on a un moyen d'avoir les constantes avec une précison *)
(* arbitraire : *)
 
(* let val_n n s = match s with *)
(*      | Pi -> calcule_pi n *)
(*      | E -> calcule_e n *)
 
(* let val_10 = val_n 10 ;; *)

Un exemple à améliorer de carnet d'adresse

OCaml
type tel = string ;;
 
type personne = { nom : string ; 
                  anniv : (int * int)  option ; 
                  tel : tel list ;
                  adr : string list ;
                  email : (string * string) list
                }
 
type carnet = personne list
 
let get_adresse s =
  match s.adr with
  | [] -> None
  | a :: _ -> Some a
 
let find_adresse n (c:carnet) =
  get_adresse (List.find (fun p -> p.nom=n) c)