Planche 1

PC9
Jean-Jacques.Levy@inria.fr
http://www.polytechnique.fr/poly/~levy/
INRIA -- Rocquencourt
tel: 01 39 63 56 89


secrétariat: Catherine Bensoussan
cb@lix.polytechnique.fr
Aile 00,
LIX
tel: 34 67


http://w3.edu.polytechnique.fr/informatique/


Planche 2

Plan

  1. Analyse lexicale
  2. Analyse syntaxique
  3. Grammaires formelles
  4. Graphes
  5. Parcours en profondeur ou largeur d'abord
  6. Connexité
  7. Exercices

Planche 3

Analyse lexicale

1ère passe pour supprimer les blancs et les commentaires. Il ne reste plus qu'un flot d opérateurs, les identificateurs, les nombres, les chaînes de caractères, etc (
lexèmes).

let rec cur_char = ref ' ' 
and next_char() = cur_char := input_char(stdin);;
let is_letter c = ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');;
let is_digit c = ('0' <= c && c <= '9') ;;
let is_blank c = c = ' ' || c = '\t' || c = '\r' || c = '\n' ;;
let skip_blanks () = while is_blank (!cur_char) do next_char() done;;

let next_ident() = 
   let buf = String.make 100 ' ' and i = ref 0 in 
   while is_letter (!cur_char) || is_digit (!cur_char) do
       buf.[!i] <- !cur_char; incr i;
       next_char()
   done;
   sub_string buf 0 !i;;

let next_number() = 
   let res = ref 0 in
   while is_digit (!cur_char) do
       res := !res*10 + Char.int (!cur_char) - Char.int ('0');
       next_char()
   done;
   !res;;

let erreur s = failwith ("Erreur: " ^ s) ;;
Il existe des outils pour générer automatiquement les analyseurs lexicaux: lex de M. Lesk ou camllex. La théorie se fait avec les automates finis (ou expressions régulières et langages réguliers).


Planche 4

Analyse lexicale -- programme

On ne garde plus qu'une suite de lexèmes, encore appelés tokens. On écrit une fonction
next_lexem qui saute les espaces et retourne le prochain lexem dans la variable globale cur_item.

type lexem = 
      L_Ident of string  (* identificateur *)
    | L_Nb of int        (* nombre *)
    | L_Plus             (* + *)        | L_Moins            (* - *)
    | L_Mult             (* * *)        | L_Div              (* / *)
    | L_Opar             (* ( *)        | L_Fpar             (* ) *)
    | L_Assign1          (* := *)       | L_Assign2          (* <- *)
    | L_EOF              (* end of file *) ;;

let cur_lexem = ref L_EOF   and  next_lexem () = 
   cur_lexem := try
     skip_blanks ();
     match !cur_char with
      'a'..'z' | 'A'..'Z' -> L_Ident(next_ident ())
     |'0'..'9' -> L_Nb(next_number ())
     |'+' -> begin next_char(); L_Plus end
     |'-' -> begin next_char(); L_Moins end
     |'*' -> begin next_char(); L_Mult end
     |'/' -> begin next_char(); L_Div end
     |':' -> begin next_char(); 
             if !cur_char <> '=' then L_Div
             else begin next_char(); L_Assign1 end
             end
     |'(' -> begin next_char(); L_Opar end
     |')'  -> begin next_char(); L_Fpar end
     | _ -> erreur "forbidden char"
   with
   End_of_file -> L_EOF;;

Planche 5

Analyse syntaxique des expressions arithmétiques

A partir d'une chaîne de caractères tapée sur l'input, comme
(x + 1) * (3 * x + 2) , on veut générer l'arbre de syntaxe abstraite suivant (cf PC8 pour le type des termes).

let rec terme () =
  let a = produit () in
  match !cur_lexem with
    L_Plus -> begin next_lexem(); Add (a, terme ()) end
  | L_Moins -> begin next_lexem(); Sub (a, terme ()) end
  | _  -> a

and produit () =
  let a = facteur () in
  match !cur_lexem with
    L_Mult -> begin next_lexem(); Mul (a, produit ()) end
  | L_Div -> begin next_lexem(); Div (a, produit ()) end
  | _  -> a

and facteur () =
  match !cur_lexem with
    L_Opar -> begin next_lexem(); 
        let a = terme () in
           if !cur_lexem = L_Fpar then begin next_lexem(); a end
           else erreur "missing right parenthesis" end
  | L_Ident(s) ->  begin next_lexem(); Var (s) end
  | L_Nb (x) ->  begin next_lexem(); Const (x) end
  | _  ->  erreur "forbidden character";;
Il existe des outils pour générer automatiquement les analyseurs syntaxique: yacc de S. Johnson ou camlyacc. La théorie se fait avec les automates à pile (ou langages context-free, aussi appelés algébriques).
Planche 6

Grammaires et langages formels

L'ensemble des mots sur un alphabet
S est noté S *. Si a Î S * et b Î S, alors ab Î S est le mot obtenu par concaténation de a et de b.

Une grammaire (context-free)
G=(S, VN, S, P) est définie par un alphabet S, un ensemble de variables (non-terminales) VN, un axiome S Î VN et un ensemble fini de règles de production P de la forme E ® aE Î VN et a Î (S È VN)*.

Une dérivation
a ®* b est une chaîne a = a0 ® a1 ® ··· an = bn ³ 0 et pour 0 £ i < n, on a ai = uEv et ai+1 = u g v pour un E ® g Î P et deux mots u, v de (S È VN)*.

Le langage généré par
G est l'ensemble LG={w | wÎS *, S ®*w }.

A chaque mot reconnu, on peut associer un arbre syntaxique (différent de l'arbre de syntaxe abstraite).



Planche 7

Exemples de grammaire

S={(, ) }, VN={S },
S ® SS      S ® (S)      S ® ()

S={  x, +, -, *, /, (, )  },
VN={T, P, F },
T ® P+T      T ® P-T      T ® P
P ® F*P      P ® F/P      P ® F
F ® x      F ® (T)

Dessiner l'arbre syntaxique pour (x+1)*(3*x+2)

La syntaxe de Java, (O)Caml, Pascal, C, C++ est décrite en BNF (
Backus Naur Form), forme industrielle de la notion de grammaire formelle.


Planche 8

Graphes

Un graphe
G=(V,E) a un ensemble de sommets V et d'arcs EÌ V × V. Un arc v=(n1,n2) a pour origine org(v) = n1 et pour extrémité ext(v) = n2. Un exemple est le plan des rues de Paris ou le plan du métro.

G=(N,V) est un graphe non orienté ssi (n1,n2) Î V implique (n2,n1) Î V. Par exemple, les couloirs de l'X.

Un chemin est une suite d'arcs
v1, v2, ...vn, telle que org(vi+1) = ext(vi) pour 1 £ i < n, où n ³ 0. Un circuit (ou cycle) est un chemin où ext(vn) = org(v1). Un arbre est donc un graphe orienté sans cycle, il existe des graphes orientés sans cycles plus généraux (dag).

Un exemple de dag est celui des relations de dépendances inter modules pour la création d'un projet informatique. (
Makefile)


Planche 9

Représentations d'un graphe

En gros, deux méthodes:


Planche 10

Parcours en profondeur d'abord, Depth-First Search

R. Tarjan (1972) a montré que c'était une méthode très utile sur les graphes. Son temps est en
O(V+E), ie linéaire dans le nombre de noeuds et d'arcs.

let num = Array.make (Array.length graphe) (-1) and id = ref (-1);;

let rec dfs k = 
  incr id;
  num.(k) <- !id;
  List.iter (function x -> if num.(x) = -1 then dfs (x))
    graphe.(k);;

let visit () =
  for i=0 to Array.length graphe - 1 do
    if num.(i) = -1 then dfs i;
  done;;
Aussi appelé arborescences de Trémaux dans le polycopié.


Planche 11

Arbres de recouvrement

L'arbre des appels récursifs à
dfs est un arbre de recouvrement (spanning tree). Les arcs d'un arc sont de 3 sortes:

  1. d'un noeud de l'arbre à un de ses fils dans l'arbre

  2. d'un noeud de l'arbre à un de ses ancètres dans l'arbre

  3. d'un noeud de l'arbre à un de ses descendants non direct dans l'arbre

  4. d'un noeud à un de ses cousins dans l'arbre
[Dans dfs d'un graphe non orienté, il n'y a que les arcs du type 1-2.]


Planche 12

Parcours en largeur d'abord, Breadth-First Search

let num = Array.make (Array.length graphe) (-1) and id = ref (-1);;

let bfs q = 
  while not (FIFO.vide(q)) do
    let k = FIFO.supprimer(q) in
    incr id;
    num.(k) <- !id;
    List.iter (function x -> if num.(x) = -1 then 
      begin FIFO.ajouter x q; num.(x) <- -2 end)
      graphe.(k)
  done;;

let visit() =
  let q = FIFO.make() in
  for i=0 to Array.length graphe - 1 do
    if num.(i) = -1 then 
       begin FIFO.ajouter i q; num.(i) <- -2; bfs q end;
  done;;
Itératif et plus compliqué que le parcours en profondeur d'abord.


Planche 13

Quelques problèmes standards


Planche 14

Sortie de labyrinthe

On cherche un chemin de
d à s.

let rec chemin d s =
  num.(d) <- true; 
  let p = ref [] in
  List.iter (function x -> 
      if not num.(x) && !p = [] then 
        if x = s then p := [s] else p := chemin x s)
     graphe.(d);
  if !p = [] then [] else d :: !p;;
Comment faire la sortie par le chemin le plus court?

Comment trouver et imprimer tous les chemins?



Planche 15

Bi-connectivité

Dans un graphe non orienté, existe-t-il 2 chemins différents entre toute paire de noeuds? Un point d'articulation est un point qui sépare le graphe en parties non connexes si on l'enlève. Un graphe sans point d'articulation est un graphe bi-connexe. La fonction suivante (en dfs) imprime les points d'articulation.

let min = ref nMax;;

let rec visit k = 
  incr id;
  num.(k) <- !id; min := !id;
  List.iter (function x -> 
      if num.(x) = -1 then begin
        let m = visit x in
        if m < !min then min := m;
        if m >= num.(k) then printf "%d " k
      end else
      if num.(x) < !min then min := num.(x))
     graphe.(k);
  !min;;

Planche 16

Exercices en TD


This document was translated from LATEX by HEVEA.