Cours 6

Htmligure
Version & licenses
Creative Commons License

Exemple d'utilisation des foncteurs.

Guyslain
Thursday, 21 July 2016

Retour au sommaire.

Exemple : l'algorithme de Floyd-Wharshall, une orgie de foncteurs.

À titre d'exemple d'utilisation des foncteurs, on peut considérer l'algorithme de Floyd-Warshall. L'un de ses avatars permet de calculer les distances entre toute paire de sommets d'un graphe. Mais l'algorithme est plus général: il permet aussi de calculer le langage reconnu par un automate, de résoudre des systèmes d'équations linéaires, etc.

La version générale travaille sur une matrice dont les coefficients sont dans une algèbre de Kleene, c'est-à-dire un semi-anneau auquel on ajoute un opérateur *. On commence donc pour poser des interfaces pour ces deux notions :

  1. module type Semiring = sig
  2. type t
  3. val zero : t
  4. val one : t
  5. val add : t -> t -> t
  6. val mult : t -> t -> t
  7. val pp : Format.formatter -> t -> unit
  8. end

  9. module type KleeneAlgebra = sig
  10. module S : Semiring
  11. val star : S.t -> S.t
  12. end

Dans Semiring, on ajoute une fonction pp de pretty-printing. Cela permettra plus tard de créer automatiquement les pretty-printers pour les matrices. Dans KleeneAlgebra, nous aurions pu plutôt inclure Semiring (avec include Semiring). Nous aurons cependant plus tard besoin de faire directement référence à la restriction au semi-anneau d'une algèbre de Kleene, donc nous utilisons un sous-module.

Nous pouvons maintenant définir l'interface d'une matrice carrée. Dans cet exemple nous allons fixé la dimension en utilisant un module de paramétrage. Ainsi des matrices de dimensions différentes appartiendront à des modules différents. Un avantage est qu'on garanti ainsi que les additions et les multiplications matricielles sont toujours définis : pas de risque d'additionner des matrices de dimensions différentes, l'inférence de type ne l'acceptera pas.

  1. module type MatrixDim = sig
  2. val dim : int
  3. end

  4. module type Matrix = sig
  5. type t
  6. type coefficient
  7. module D : MatrixDim
  8. val create : (int -> int -> S.t) -> t
  9. val get_coef : t -> int -> int -> S.t
  10. val pp : Format.formatter -> t -> unit
  11. end

On se contente d'avoir la dimension, une fonction de construction et une fonction d'accès aux coefficients. Pour créer une représentation, il nous faut un type pour les coefficients ayant aussi une fonction de pretty-printing. On va donc définir une interface pour un type pretty-printable. Par ailleurs, on peut facilement représenter une matrice comme une fonction des paires d'entiers vers les coefficients. Pour plus d'efficacité, on va néanmoins stocker les coefficients dans une structure de données : un dictionnaire des paires d'entiers vers les coefficients. Il faut donc définir cela :

  1. module type PrintableType = sig
  2. type t
  3. val pp : Format.formatter -> t -> unit
  4. end

  5. module IntPair = struct
  6. type t = int * int
  7. let compare (x1,y1) (x2,y2) = if x1 = x2 then y1 - y2 else x1 - x2
  8. end

  9. (* Dictionaries with keys of type [int * int] *)
  10. module IPMap = Map.Make(IntPair)


  11. (* Double functor: matrix dimension + coefficients *)
  12. module MakeMatrix :
  13. functor (D : MatrixDim) ->
  14. functor (Coef : PrintableType) -> Matrix with type coefficients = Coef.t
  15. =
  16. functor (D : MatrixDim) ->
  17. functor (Coef : PrintableType) ->
  18. struct

  19. type t = Coef.t IPMap.t
  20. type coefficients = Coef.t
  21. module D = D

  22. let get_coef mat i j = IPMap.find (i,j) mat

  23. let indices = List.range ~stop:`inclusive 1 D.dim
  24. let positions = List.cartesian_product indices indices

  25. let create fct =
  26. List.fold_left
  27. ~f:(fun map (i,j) -> IPMap.add (i,j) (fct i j) map)
  28. ~init:IPMap.empty
  29. positions

  30. let pp_line mat ppf line =
  31. let open Format in
  32. fprintf ppf "@[<h 0>%a@]"
  33. (pp_print_list ~pp_sep:(fun ppf () -> pp_print_break ppf 3 0) Coef.pp)
  34. (List.map ~f:(get_coef mat line) indices)

  35. let pp ppf mat =
  36. let open Format in
  37. fprintf ppf "@[<v 0>%a@]"
  38. (pp_print_list ~pp_sep:pp_print_cut (pp_line mat))
  39. indices

  40. end

Ce qui est intéressant avec les matrices est qu'elles forment elles-mêmes un semi-anneau, si les coefficients forment un semi-anneau. On peut le prouver en donnant un foncteur, prenant une représentation des matrices, un semi-anneau sur les coefficients, et produisant un semi-anneau des matrices. Autrement dit, on va écrire l'addition et la multiplication matricielle.


  1. module type MatrixSemiring =
  2. sig
  3. include Matrix
  4. module S : Semiring with type t = coefficients
  5. val zero : t
  6. val one : t
  7. val add : t -> t -> t
  8. val mult : t -> t -> t
  9. end

  10. module MakeMatrixSemiring :
  11. functor (M : Matrix) ->
  12. functor (S : Semiring with type t = M.coefficients) ->
  13. MatrixSemiring
  14. with module D = M.D
  15. and type t = M.t
  16. and type coefficients = M.coefficients
  17. =
  18. functor (M : Matrix) ->
  19. functor (S : Semiring with type t = M.coefficients) ->
  20. struct
  21. include M
  22. module S = S
  23. let (+) = S.add
  24. let ( * ) = S.mult

  25. let zero = create (fun i j -> S.zero)
  26. let one = create (fun i j -> if i = j then S.one else S.zero)

  27. let add mat1 mat2 =
  28. create (fun i j -> get_coef mat1 i j + get_coef mat2 i j)

  29. let mult mat1 mat2 =
  30. create @@ fun i j ->
  31. List.range ~stop:`inclusive 1 D.dim
  32. |> List.map ~f:(fun k -> get_coef mat1 i k * get_coef mat2 k j)
  33. |> List.reduce ~f:(+)
  34. |> Core.Std.Option.value ~default:S.zero
  35. end

Ceci dit, nous n'avons pas besoin des opérateurs sur les matrices pour l'algorithme de Floyd-Warshall. Nous avons simplement besoin d'une matrice et d'une algèbre de Kleene sur les coefficients de cette matrice. Nous pouvons donc écrire un autre foncteur pour obtenir l'algorithme (on l'appelle close car il calcule une fermeture transitive).

  1. module MakeFloydWarshall :
  2. functor (K : KleeneAlgebra) ->
  3. functor (M : Matrix with type coefficients = K.S.t) ->
  4. sig
  5. val close : M.t -> M.t
  6. end
  7. = functor (K : KleeneAlgebra) ->
  8. functor (M : Matrix with type coefficients = K.S.t) ->
  9. struct
  10. let (+) = K.S.add
  11. let ( * ) = K.S.mult

  12. let define succ_k mat_k i j =
  13. M.get_coef mat_k i j
  14. + M.get_coef mat_k i succ_k
  15. * K.star (M.get_coef mat_k succ_k succ_k)
  16. * M.get_coef mat_k succ_k j

  17. let close matrix =
  18. List.fold_left
  19. ~f:(fun mat_k succ_k ->
  20. M.create (define succ_k mat_k)
  21. )
  22. ~init:matrix
  23. (List.range ~stop:`inclusive 1 M.D.dim)

  24. end

Voilà pour la partie algorithmique : en présence de semi-anneaux ou d'algèbres de Kleene, on peut dériver des matrices avec MakeMatrix, puis un semi-anneau matriciel avec MakeMatrixSemiring ou un algorithme de Floyd-Warshall avec MakeFloydWarshall.

Essayons avec plusieurs algèbres de Kleene. Commençons par un calcul de distance dans un graphe orienté :

  1. module MinPlus =
  2. struct
  3. type t = Infty | Int of int
  4. let zero = Infty
  5. let one = Int 0
  6. let add i j = match i, j with
  7. | Int i, Int j -> Int (min i j)
  8. | Infty, _ -> j
  9. | _, Infty -> i
  10. let mult i j = match i, j with
  11. | Int i, Int j -> Int (i+j)
  12. | _ -> Infty
  13. let pp ppf = function
  14. | Int i -> Format.fprintf ppf "%3d" i
  15. | Infty -> Format.pp_print_string ppf "+oo"
  16. end

  17. module KleeneMinPlus =
  18. struct
  19. module S = MinPlus
  20. let star i = S.Int 0
  21. end

  22. module D8 = struct
  23. let dim = 8
  24. end

On utilise le semi-anneau $(\min,+)$ : on veut calculer le minimum de la somme des longueurs de chaque chemin. L'étoile est utilisée pour boucler sur un sommet, dans cette application, boucler sur un sommet ne coûte rien : cela veut dire faire du sur-place. On applique maintenant les foncteurs :

  1. module MatrixMinPlus8 = MakeMatrix(D8)(MinPlus)
  2. module SemiringMinPlus8 = MakeMatrixSemiring(MatrixMinPlus8)(MinPlus)
  3. module FWMinPlus8 = MakeFloydWarshall(KleeneMinPlus)(MatrixMinPlus8)

Testons en écrivant une fonction de génération aléatoire d'une matrice. Il suffit d'une fonction aléatoire pour les coefficients, et d'appeler MatrixMinPlus8.create avec.

  1. let random_MinPlus_coef p =
  2. if Random.float 1. < p then MinPlus.Int 1 else MinPlus.Infty
  3. let random_mat p =
  4. MatrixMinPlus8.create (fun i j -> random_MinPlus_coef p)

  5. let mat = random_mat 0.3
  6. let mat_close = FWMinPlus8.close mat

On installe le formatteur pour les matrices, puis on teste :

  1. # #install_printer MatrixMinPlus8.pp;;
  2. # let mat = random_mat 0.3;;
  3. val mat : MatrixMinPlus8.t =
  4. +oo 1 1 +oo +oo +oo +oo +oo
  5. +oo +oo 1 +oo +oo +oo +oo +oo
  6. 1 1 +oo +oo 1 +oo +oo +oo
  7. 1 +oo +oo +oo 1 +oo +oo 1
  8. 1 +oo +oo 1 +oo +oo +oo +oo
  9. +oo 1 +oo 1 +oo +oo +oo +oo
  10. +oo 1 +oo +oo +oo +oo +oo +oo
  11. +oo +oo +oo +oo +oo +oo +oo +oo
  12. # let mat_close = FWMinPlus8.close mat;;
  13. val mat_close : MatrixMinPlus8.t =
  14. 2 1 1 3 2 +oo +oo 4
  15. 2 2 1 3 2 +oo +oo 4
  16. 1 1 2 2 1 +oo +oo 3
  17. 1 2 2 2 1 +oo +oo 1
  18. 1 2 2 1 2 +oo +oo 2
  19. 2 1 2 1 2 +oo +oo 2
  20. 3 1 2 4 3 +oo +oo 5
  21. +oo +oo +oo +oo +oo +oo +oo +oo

Un dessin permet ensuite de vérifier que le résultat est bien la matrice des distances du graphe. Il serait néanmoins plus intéressant de connaître aussi les plus courts chemins. Pour cela, il suffit de changer de semi-anneau. Profitons-en pour généraliser l'algorithme pour le cas où les arêtes possèdent des longueurs arbitraires positives. Pour ne pas choisir entre des longueurs entières ou flottantes, on peut à nouveau introduire un foncteur :

  1. module MakeMinPlusPath = functor (SR : Semiring) ->
  2. struct
  3. module S =
  4. struct
  5. let (<=) x y = SR.add x y = x (* we need add to be a minimum *)

  6. type t =
  7. | NoPath
  8. | Path of SR.t * (int * int) list

  9. let zero = NoPath
  10. let one = Path (SR.one, [])

  11. let add p1 p2 =
  12. match p1, p2 with
  13. | Path (len1,_), Path (len2, _) when len1 <= len2 -> p1
  14. | Path _, Path _ -> p2
  15. | NoPath,_ -> p2
  16. | _,NoPath -> p1

  17. let mult p1 p2 =
  18. match p1, p2 with
  19. | Path (len1,p1), Path (len2,p2) -> Path (SR.mult len1 len2, p1 @ p2)
  20. | _ -> NoPath


  21. let pp_edge ppf (i,j) = Format.fprintf ppf "(%d,%d)" i j
  22. let pp_edges ppf list =
  23. let open Format in
  24. pp_print_list ~pp_sep:(fun ppf () -> fprintf ppf ";") pp_edge ppf list
  25. let pp ppf = function
  26. | NoPath -> Format.pp_print_string ppf "ø"
  27. | Path (len,list) ->
  28. Format.fprintf ppf "%a:[%a]" SR.pp len pp_edges list

  29. end

  30. let star p = S.one
  31. end

Pour l'instancier sur des longueurs flottantes, on crée un semi-anneau $(\min,+)$ des flottants, et on applique le foncteur MakeMinPlusPath.

  1. module FloatSR =
  2. struct
  3. type t = float
  4. let zero = infinity
  5. let one = 0.
  6. let add = min
  7. let mult = (+.)
  8. let pp = Format.pp_print_float
  9. end

  10. module MinPlusPath = MakeMinPlusPath(FloatSR)

Nous sommes prêt à produire les matrices et l'algorithme de Floyd-Warshall :

  1. module D5 = struct let dim = 5 end
  2. module MatrixMinPlusPath5 = MakeMatrix(D5)(MinPlusPath.S)
  3. module SemiringMinPlusPath5 =
  4. MakeMatrixSemiring(MatrixMinPlusPath5)(MinPlusPath.S)
  5. module FWMinPlusPath5 = MakeFloydWarshall(MinPlusPath)(MatrixMinPlusPath5)

De nouveau, on ajoute un générateur aléatoire et on teste sur un exemple.

  1. let random_MinPlusPath_coef p i j =
  2. if i = j then
  3. MinPlusPath.S.(Path (0.,[]))
  4. else if Random.float 1. < p then
  5. MinPlusPath.S.(Path (Random.float 10., [(i,j)]))
  6. else
  7. MinPlusPath.S.NoPath

  8. let random_float_mat p =
  9. MatrixMinPlusPath5.create (random_MinPlusPath_coef p)


  10. (* in toplevel: *)
  11. # #install_printer MatrixMinPlusPath5.pp;;
  12. # let mat = random_float_mat 0.4;;
  13. val mat : MatrixMinPlusPath5.t =
  14. 0.:[] ø ø 0.819736241165:[(1,4)] ø
  15. 0.407159120634:[(2,1)] 0.:[] ø ø 5.61633446903:[(2,5)]
  16. 6.9831845109:[(3,1)] ø 0.:[] ø ø
  17. ø 2.61599931405:[(4,2)] ø 0.:[] ø
  18. ø ø 2.13522490529:[(5,3)] ø 0.:[]
  19. # let mat_close = FWMinPlusPath5.close mat;;
  20. val mat_close : MatrixMinPlusPath5.t =
  21. 0.:[]
  22. 3.43573555521:[(1,4);(4,2)]
  23. 11.1872949295:[(1,4);(4,2);(2,5);(5,3)]
  24. 0.819736241165:[(1,4)]
  25. 9.05207002424:[(1,4);(4,2);(2,5)]

  26. 0.407159120634:[(2,1)]
  27. 0.:[]
  28. 7.75155937432:[(2,5);(5,3)]
  29. 1.2268953618:[(2,1);(1,4)]
  30. 5.61633446903:[(2,5)]

  31. 6.9831845109:[(3,1)]
  32. 10.4189200661:[(3,1);(1,4);(4,2)]
  33. 0.:[]
  34. 7.80292075206:[(3,1);(1,4)]
  35. 16.0352545351:[(3,1);(1,4);(4,2);(2,5)]

  36. 3.02315843468:[(4,2);(2,1)]
  37. 2.61599931405:[(4,2)]
  38. 10.3675586884:[(4,2);(2,5);(5,3)]
  39. 0.:[]
  40. 8.23233378308:[(4,2);(2,5)]

  41. 9.11840941618:[(5,3);(3,1)]
  42. 12.5541449714:[(5,3);(3,1);(1,4);(4,2)]
  43. 2.13522490529:[(5,3)]
  44. 9.93814565735:[(5,3);(3,1);(1,4)]
  45. 0.:[]

On termine avec un exemple sensiblement différent : construire l'expression régulière reconnue par un automate fini. Pour cela, il suffit d'utiliser Floyd-Warshall sur l'algèbre de Kleene usuelle sur les langages réguliers. Cette application a un problème : les expressions régulières générées peuvent être extrêmement longues. Pour éviter cela, nous allons regarder des automates tout petits et utiliser des règles de simplification sur les expressions régulières. Cela rend la définition des expressions régulières assez complexes. Par ailleurs, on ne veut pas imposer le type des lettres de l'alphabet, donc nous allons encore ajouter un foncteur !

  1. module type Alphabet =
  2. sig
  3. type letter
  4. val pp : Format.formatter -> letter -> unit
  5. end

On ajoute quelques fonctions utilitaires puis le foncteur pour créer le type des expressions régulières sur un alphabet :


  1. type 'elt at_least_two_elts_list =
  2. ('elt * 'elt list * 'elt)

  3. type 'elt list_ends =
  4. | No_ends
  5. | Single_end of 'elt
  6. | Double_ends of 'elt * 'elt list * 'elt

  7. let rec insert_ends head tail =
  8. match observe_list_ends tail with
  9. | No_ends -> Single_end head
  10. | Single_end last -> Double_ends (head,[],last)
  11. | Double_ends (snd,middle,last) -> Double_ends (head,snd::middle,last)
  12. and observe_list_ends = function
  13. | [] -> No_ends
  14. | head::tail -> insert_ends head tail



  15. module MakeRegexp :
  16. functor (A : Alphabet) ->
  17. sig
  18. type regexp =
  19. | Empty
  20. | Epsilon
  21. | Letter of A.letter
  22. | Or of regexp at_least_two_elts_list
  23. | Concat of regexp at_least_two_elts_list
  24. | Star of regexp
  25. include KleeneAlgebra with type S.t = regexp
  26. end
  27. = functor (A : Alphabet) ->
  28. struct

  29. type regexp =
  30. | Empty
  31. | Epsilon
  32. | Letter of A.letter
  33. | Or of regexp at_least_two_elts_list
  34. | Concat of regexp at_least_two_elts_list
  35. | Star of regexp

  36. let zero = Empty
  37. let one = Epsilon


  38. let to_or list =
  39. match observe_list_ends list with
  40. | No_ends -> Empty
  41. | Single_end elt -> elt
  42. | Double_ends (head,middle,last) -> Or (head, middle, last)
  43. let of_triple (head,middle,last) =
  44. head :: middle @ [last]

  45. let to_concat list =
  46. match observe_list_ends list with
  47. | No_ends -> Epsilon
  48. | Single_end elt -> elt
  49. | Double_ends (head,middle,last) -> Concat (head,middle,last)

  50. let merge = List.merge ~cmp:compare

  51. let rec contains regexp1 regexp2 =
  52. regexp1 = regexp2 ||
  53. match regexp1, regexp2 with
  54. | _, Star e when contains regexp1 e -> true
  55. | Star e1, Star e2 -> contains e1 regexp2
  56. | _, Or (head,middle,tail) ->
  57. List.exists ~f:(contains regexp1) (head::tail::middle)
  58. | don't_know -> false

  59. let rec insert e = function
  60. | head::tail when contains head e -> e::tail
  61. | head::tail as list when contains e head -> list
  62. | head::tail when head < e -> head :: insert e tail
  63. | list -> e::list

  64. let rec add_concat ((head1,mid1,tail1) as re1) ((head2,mid2,tail2) as re2) =
  65. if head1 = head2 then
  66. mult head1 (add (to_concat (mid1@[tail1])) (to_concat (mid2@[tail2])))
  67. else if tail1 = tail2 then
  68. mult (add (to_concat (head1::mid1)) (to_concat (head2::mid2))) tail1
  69. else Or (Concat re1,[],Concat re2)

  70. and add e1 e2 = match e1, e2 with
  71. | Empty, e
  72. | e, Empty -> e
  73. | Epsilon, Star e
  74. | Star e, Epsilon -> Star e
  75. | Epsilon, Concat (Star e1,[],e2) when e1 = e2 -> Star e1
  76. | Epsilon, Concat (e1,[],Star e2) when e1 = e2 -> Star e1
  77. | Concat (e1,[],Star e2), Epsilon when e1 = e2 -> Star e1
  78. | Concat (Star e2,[],e1), Epsilon when e1 = e2 -> Star e1
  79. | e, Concat (head,middle,last) when e = head ->
  80. mult e (add Epsilon (to_concat (middle@[last])))
  81. | e, Concat (head,middle,last) when e = last ->
  82. mult (add Epsilon (to_concat (head::middle))) e
  83. | Concat (head,middle,last), e when e = head ->
  84. mult e (add Epsilon (to_concat (middle@[last])))
  85. | Concat (head,middle,last), e when e = last ->
  86. mult (add Epsilon (to_concat (head::middle))) e
  87. | e1, e2 when e1 = e2 -> e1
  88. | Or regexps1, Or regexps2 ->
  89. to_or (merge (of_triple regexps1) (of_triple regexps2))
  90. | e, Or regexps
  91. | Or regexps, e -> to_or (insert e (of_triple regexps))
  92. | Concat regexps1, Concat regexps2 -> add_concat regexps1 regexps2
  93. | _, _ -> Or (e1,[],e2)

  94. and mult e1 e2 = match e1, e2 with
  95. | Epsilon, e
  96. | e, Epsilon -> e
  97. | Empty, _
  98. | _, Empty -> Empty
  99. | Concat (head1,mid1,last1), Concat (head2,mid2,last2) ->
  100. Concat (head1,mid1 @ [last1; head2] @ mid2,last2)
  101. | Concat (head,mid,last), e -> Concat (head,mid@[last],e)
  102. | Star e1, Star e2 when contains e1 e2 -> Star e2
  103. | Star e1, Star e2 when contains e2 e1 -> Star e1
  104. | _ -> Concat (e1,[],e2)

  105. and star = function
  106. | Empty -> Epsilon
  107. | Epsilon -> Epsilon
  108. | e -> Star e


  109. let rec pp_re ppf = function
  110. | Or regexps ->
  111. Format.pp_print_list
  112. ~pp_sep:(fun ppf () -> Format.fprintf ppf "@,|")
  113. pp_re
  114. ppf
  115. (of_triple regexps)
  116. | e -> pp_term ppf e
  117. and pp_term ppf = function
  118. | Concat regexps ->
  119. Format.pp_print_list
  120. pp_term
  121. ppf
  122. (of_triple regexps)
  123. | e -> pp_factor ppf e
  124. and pp_factor ppf = function
  125. | Empty -> Format.pp_print_string ppf "ø"
  126. | Epsilon -> Format.pp_print_string ppf "e"
  127. | Letter a -> A.pp ppf a
  128. | Star e -> Format.fprintf ppf "@[%a*@]" pp_factor e
  129. | e -> Format.fprintf ppf "@[(%a)@]" pp_re e

  130. module S = struct
  131. type t = regexp
  132. let zero = zero
  133. let one = one
  134. let add = add
  135. let mult = mult
  136. let pp = pp_re
  137. end
  138. end

Maintenant, on utilise les différents foncteurs pour obtenir notre instance de l'algorithme de Floyd-Warshall, appliqué aux expressions régulières sur un alphabet à deux lettres :

  1. module D2 = struct
  2. let dim = 2
  3. end

  4. module AB = struct
  5. type letter = A | B
  6. let pp ppf = function
  7. | A -> Format.pp_print_string ppf "a"
  8. | B -> Format.pp_print_string ppf "b"
  9. end

  10. module ReAB = MakeRegexp(AB)

  11. module Automata = MakeMatrix(D2)(ReAB.S)
  12. module FWAutomata = MakeFloydWarshall(ReAB)(Automata)

On crée un automate, et on teste :

  1. # #install_printer Automata.pp;;
  2. # let aut1 =
  3. Automata.create
  4. (fun i j -> match i,j with
  5. | 1,1 -> ReAB.Letter AB.A
  6. | 1,2 -> ReAB.Letter AB.B
  7. | 2,1 -> ReAB.Empty
  8. | 2,2 -> ReAB.(S.add (Letter AB.A) (Letter AB.B))
  9. | _ -> ReAB.Empty
  10. );;
  11. val aut1 : Automata.t =
  12. a b
  13. ø a|b
  14. # let aut2 = FWAutomata.close aut1;;
  15. val aut2 : Automata.t =
  16. aa* a*b(a|b)*
  17. ø (a|b)(a|b)*

Avec des automates aléatoires :

  1. let random_arc i j =
  2. match Random.int 5 with
  3. | 0 -> ReAB.Letter AB.A
  4. | 1 -> ReAB.Letter AB.B
  5. | 2 -> ReAB.S.add (ReAB.Letter AB.A) (ReAB.Letter AB.B)
  6. | 3 -> ReAB.Epsilon
  7. | _ -> ReAB.Empty
  8. let random_automaton () =
  9. Automata.create random_arc


  10. (* In toplevel: *)
  11. # let aut3 = random_automaton ();;
  12. val aut3 : Automata.t =
  13. e ø
  14. b a
  15. # let aut4 = FWAutomata.close aut3;;
  16. val aut4 : Automata.t =
  17. e ø
  18. a*b aa*
  19. # let aut5 = random_automaton ();;
  20. val aut5 : Automata.t =
  21. e a|b
  22. a|b b
  23. # let aut6 = FWAutomata.close aut5;;
  24. val aut6 : Automata.t =
  25. e|(a|b)(b|(a|b)(a|b))*(a|b) (a|b)(b|(a|b)(a|b))*
  26. (b|(a|b)(a|b))*(a|b) (b|(a|b)(a|b))(b|(a|b)(a|b))*

Impeccable: chaque coefficient $c_{i,j}$ est une expression régulière pour le language des mots permettant d'aller de l'état $i$ vers l'état $j$, comme attendu.

Retour au sommaire.