Table of Contents

LANGAGES ET TRADUCTEURS – Chapitre 2 Jean-François Monin, 2021

Arbres

Les données sur lesquelles on travaillera seront organisées en arbre.

Pour information, on peut formaliser cette notion au moyen des mathématiques usuelles en théorie des ensembles. Ce ne sont rien d'autre, au final, que des encodages fabriqués à partir d'entiers et de suites d'entiers.

Pour des informaticiens, l'intuition obtenue en dessinant des arbres est suffisante : on sait comment représenter de telles structures en mémoire au moyen de cellules chaînées par des pointeurs. Plus spécifiquement, on se focalise sur le cas ou ces chaînages ne comportent pas de cycles. Programmer avec des arbres revient alors à programmer avec des structures chaînées sans se préoccuper de considérations liées à la gestion mémoire (allocation et désallocation) qui sont traitées automatiquement par les couches basses.

La structure des arbres considérés est définie par un type inductif

1 Exemples intuitifs

1.1 Listes d'entiers

(2020 : déjà vu en PF) La liste des 3 premiers entiers consécutifs se dessine :

 Cons                    ::
 /  \                   /  \
1   Cons               1    ::
    /  \                   /  \
   2   Cons               2    ::
       /  \                   /  \
      3    Nil               3    []

Définition du type correspondant

1.1.1 En OCaml

type listN = Nil  |  Cons of int * listN

Notation linéaire : Cons (1, (Cons (2, (Cons (3, Nil)))))

ou 1 :: 2 :: 3 :: []

1.1.2 En Coq

Inductive listN : Set :=
  | Nil : listN
  | Cons : nat -> listN  -> listN
.

(Cons 1 (Cons 2 (Cons 3 Nil)))

Différences avec OCaml :

  • les noms des constructeurs peuvent ne pas commencer par une majuscule

Inductive listN = nil : listN | cons : nat -> listN -> listN

  • chaque constructeur est traité comme une fonction et les arguments sont pris un à un (ils sont pris en bloc en OCaml) Notation linéaire : Cons 1 (Cons 2 (Cons 3 Nil)) ;
  • déclaration du type de listN (les types aussi ont un type) ; en Coq il y a un type prédéfini pour abriter des types comme listN, qui s'appelle Set ; on y reviendra plus tard, il suffit à ce stade de savoir l'indiquer dans la déclaration de listN ;
  • ponctuation.

1.2 AST (Abstract Syntactic Trees) d'expressions arithmétiques

(2020 : vu au TD1)

Ce qui s'écrit usuellement (1 + 2) * 3 peut se dessiner :

      ⊗
    /   \
  ⊕      3
 / \
1   2

Ou encore, en étiquetant les nœuds par Apl et Amu au lieu de, respectivement, ⊕ et ⊗ :

     Amu
    /   \
 Apl     3
 / \
1   2

Pour plus de rigueur et de précision, on ajoute une étiquette aux feuilles qui portent un entier, ce qui donne :

      Amu
     /   \
  Apl    Cst
  / \     |
Cst Cst   3
 |   |
 1   2

La dernière version permet de voir aexp comme un type somme que l'on peut définir en OCaml on en Coq. Inconvénient : les dessins sont un peu plus gros. Avantage : une distinction claire entre les types. On n'a plus de confusion entre le type des entiers et le type des AST d'expressions arithmétiques, ce qui simplifie le cadre conceptuel.

Définition du type correspondant

1.2.1 En OCaml

type aexp = Cst of int  |  Apl of aexp * aexp  |  Amu of aexp * aexp  

Notation linéaire : Amu (Apl (Cst (1), Cst (2)), Cst (3))

1.2.2 En Coq

Inductive aexp : Set := 
  | Cst : nat -> aexp
  | Apl : aexp -> aexp -> aexp
  | Amu : aexp -> aexp -> aexp
.

Notation linéaire : Amu (Apl (Cst 1) (Cst 2)) (Cst 3)

2 Grammaires d'arbres

Une façon de déclarer les arbres est la suivante, en s'inspirant du formalisme des grammaires hors-contexte définies sur des séquences linéaires plates.

2.1 AST (Abstract Syntactic Trees) d'expressions arithmétiques

aexp ::=  Cst   |      Apl       |      Amu
           |           / \              / \
          nat      aexp   aexp      aexp   aexp

2.2 Listes d'entiers

listN ::=  Nil   |     Cons
                       /  \
                     nat  listN

Ou bien, avec la notation vue ci-dessus au moyen de caractères spéciaux à la place de Nil et Cons :

listN ::=  []    |      ::
                       /  \
                     nat  listN

3 Calculs récursifs sur des arbres

Pour calculer une valeur à partir d'un arbre, on donne des équations lues de gauche à droite pour chacune des alternatives.

Exemple vu au TD 1 : nombre de feuilles d'un AST

Exemple : longueur d'une liste d'entiers

longueur []   =   0

              ::
longueur     /  \      =   1 + longueur l
            n    l

Notation linéaire :

longueur ( n :: l )  =  1 + longueur l

En OCaml ou en Coq, ces équations sont programmées par des expressions de filtrage.

3.1 En OCaml

let rec nbf = fun e ->
  match e with
  | Cst (n) -> 1
  | Apl (e1, e2) -> nbf e1  +  nbf e2
  | Amu (e1, e2) -> nbf e1  +  nbf e2

3.2 En Coq

Fixpoint nbf := fun e =>
  match e with
  | Cst n => 1
  | Apl e1 e2 => nbf e1  +  nbf e2
  | Amu e1 e2 => nbf e1  +  nbf e2
  end.

Author: jf

Created: 2021-10-11 lun. 12:53

Validate