Setup

#load "ocaml-talf/src/talf.cma";;
#directory "ocaml-talf/src";;
open Conj;;
open Auto;;
open Ergo;;
open Graf;;
 

Un automata finito viene definido por la 5-tupla AF = (Q,E,q0,d,F) donde:

Qconjunto de estadosestado conjunto
Ealfabeto de entradasimbolo conjunto
q0estado inicialestado
dfuncion de transicionarco_af conjunto
Fconjunto estados finalesestado conjunto

Ejercicio 1

es_afne : Auto.af bool

Implemente una función es_afne : Auto.af bool que reciba como argumento un autómata finito, y que devuelva true si se trata de un autómata que presenta alguna épsilon-transición, o false en caso contrario.

let es_afne (Af (estados, alfabeto, e_ini, arcos, e_fin)) =
  let rec aux = function
    | Conjunto [] -> false
    | _ -> true
  in aux (avanza (Terminal "") estados (Af (estados, alfabeto, e_ini, arcos, e_fin)));;
 
  • Utiliza una función auxiliar (aux) con pattern matching para verificar si hay épsilon-transiciones en los arcos del autómata.
  • Dentro de aux, invoca la función avanza con un estado terminal vacío Terminal "", y si retornara algun resultado confirmaría que sí hay épsilon-transiciones

Ejemplo

let afne = Af (
    Conjunto [Estado "0"; Estado "1"; Estado "2"; Estado "3"], (*estados*)
 
    Conjunto [Terminal "a"; Terminal "b"; Terminal "c"], (*alfabeto*)
 
    Estado "0", (*estado inicial*)
 
    Conjunto [Arco_af (Estado "0", Estado "1", Terminal "a"); (*funcion de transicion*)
    Arco_af (Estado "1", Estado "1", Terminal "b");
    Arco_af (Estado "1", Estado "2", Terminal "a");
    Arco_af (Estado "2", Estado "0", Terminal "");
    Arco_af (Estado "2", Estado "3", Terminal "c")],
 
    Conjunto [Estado "1"; Estado "3"] (*estados finales*)
  );;
 
es_afne afne;;
let afn = Af (
    Conjunto [Estado "0"; Estado "1"; Estado "2"; Estado "3"], (*estados*)
 
    Conjunto [Terminal "a"; Terminal "b"; Terminal "c"], (*alfabeto*)
 
    Estado "0", (*estado inicial*)
 
    Conjunto [Arco_af (Estado "0", Estado "1", Terminal "a"); (*funcion de transicion*)
    Arco_af (Estado "1", Estado "1", Terminal "b");
    Arco_af (Estado "1", Estado "2", Terminal "a");
    Arco_af (Estado "2", Estado "0", Terminal "a");
    Arco_af (Estado "2", Estado "3", Terminal "a");
    Arco_af (Estado "2", Estado "3", Terminal "c")],
 
    Conjunto [Estado "1"; Estado "3"] (*estados finales*)
  );;
 
es_afne afn;;

es_afn : Auto.af bool

Implemente una función es_afn : Auto.af bool que reciba como argumento un autómata finito, y que devuelva true si se trata de un autómata que presenta algún tipo de no determinismo (excepto épsilon-transiciones), o false en caso contrario.

let get_simbols estado (Conjunto arcos) =
  let rec aux arcs sim = match arcs with
    | [] -> sim
    | (Arco_af (e, d, s))::t -> if (e=estado) then (aux t (s::sim)) else (aux t sim)
  in aux arcos [];;
 

Toma un estado y un conjunto de arcos y devuelve una lista de símbolos de las transiciones salientes desde ese estado. Si hay varias transiciones con el mismo símbolo, este símbolo puede repetirse en la lista.

let es_afn (Af (Conjunto estados, alfabeto, e_ini, arcos, e_fin)) =
  let rec aux = function
    | [] -> false
    | h::t ->
       let simbols = (get_simbols h arcos) in
       if (List.length simbols) = (cardinal (conjunto_of_list(simbols))) then (*Comprobación item repetido*)
         aux t
       else
         true
  in aux estados;;
 
  • Utiliza una función auxiliar (aux) con pattern matching para iterar sobre los estados del autómata.
  • En cada iteración, obtiene la lista de símbolos de las transiciones salientes desde el estado actual utilizando la función get_simbols.
  • Luego, compara la longitud de esta lista con la cardinalidad del conjunto de símbolos. Si son diferentes, significa que hay al menos un símbolo repetido en las transiciones salientes del estado actual.
  • Si encuentra algún estado con símbolos repetidos en sus transiciones salientes, devuelve true, indicando la presencia de no determinismo en el autómata. Si recorre todos los estados y no encuentra ninguno con símbolos repetidos, devuelve false.

Ejemplo

let afn = Af (
    Conjunto [Estado "0"; Estado "1"; Estado "2"; Estado "3"], (*estados*)
 
    Conjunto [Terminal "a"; Terminal "b"; Terminal "c"], (*alfabeto*)
 
    Estado "0", (*estado inicial*)
 
    Conjunto [Arco_af (Estado "0", Estado "1", Terminal "a"); (*funcion de transicion*)
    Arco_af (Estado "1", Estado "1", Terminal "b");
    Arco_af (Estado "1", Estado "2", Terminal "a");
    Arco_af (Estado "2", Estado "0", Terminal "a");
    Arco_af (Estado "2", Estado "3", Terminal "a");
    Arco_af (Estado "2", Estado "3", Terminal "c")],
 
    Conjunto [Estado "1"; Estado "3"] (*estados finales*)
  );;
 
es_afn afn;;

es_afd : Auto.af bool

Implemente una función es_afd : Auto.af bool que reciba como argumento un autómata finito, y que devuelva true si se trata de un autómata totalmente determinista, o false en caso contrario.

let es_afd (Af (Conjunto estados, alfabeto, e_ini, arcos, e_fin)) =
  let num_estados = List.length estados in
  let num_simbolos = cardinal alfabeto in
  let num_arcos = cardinal arcos in
  let is_non_determinism = es_afn (Af (Conjunto estados, alfabeto, e_ini, arcos, e_fin)) in
  let is_full_deterministic = num_arcos = num_estados * num_simbolos in
  is_full_deterministic && (not is_non_determinism);;
 
  • Comprueba si el numero de arcos es igual al numero de estado por el numero de simbolos en el alfabeto
  • Usando la funcion es_afn calcula si el automata es determinista, y si cumple ambas premisas retorna true;

Ejemplo

let afd = Af (
    Conjunto [Estado "0"; Estado "1"; Estado "2"; Estado "3"], (*estados*)
 
    Conjunto [Terminal "a"; Terminal "b"], (*alfabeto*)
 
    Estado "0", (*estado inicial*)
 
    Conjunto [Arco_af (Estado "0", Estado "1", Terminal "a"); (*funcion de transicion*)
    Arco_af (Estado "0", Estado "0", Terminal "b");
    Arco_af (Estado "1", Estado "1", Terminal "b");
    Arco_af (Estado "1", Estado "2", Terminal "a");
    Arco_af (Estado "2", Estado "0", Terminal "a");
    Arco_af (Estado "3", Estado "3", Terminal "a");
    Arco_af (Estado "3", Estado "3", Terminal "b");
    Arco_af (Estado "2", Estado "3", Terminal "b")],
 
    Conjunto [Estado "1"; Estado "3"] (*estados finales*)
  );;
 
es_afd afd;;

Ejercicio 2

Implemente una función equivalentes : Auto.af Auto.af bool que reciba como argumentos dos autómatas finitos y que devuelva true cuando ambos autómatas acepten el mismo lenguaje, o false en caso contrario.

let rec siguiente estado simbolo = function
  | [] -> estado;
  | h :: t ->
     match h with Arco_af (origen, destino, simbolo_arco) ->
       if (origen = estado) && (simbolo_arco = simbolo) then destino
       else siguiente estado simbolo t ;;
 

Toma un estado, un símbolo y una lista de arcos y devuelve el estado siguiente después de la transición con ese símbolo desde el estado dado

let equivalentes (Af (estados1, alfabeto1, e_ini1, arcos1, e_fin1))
                 (Af (estados2, alfabeto2, e_ini2, arcos2, e_fin2)) =
  let alfabeto = Conj.union alfabeto1 alfabeto2 in
  let queue = Queue.create () in
  Queue.add (e_ini1, e_ini2) queue;
  let rec consume queue visitados =
    if Queue.is_empty queue then true
    else
      let estado_actual = Queue.pop queue in
      if Conj.pertenece estado_actual visitados then
        consume queue visitados
      else
        if not ((Conj.pertenece (fst estado_actual) e_fin1) = (Conj.pertenece (snd estado_actual) e_fin2)) then
          false
        else
          let rec procesar = function
            | [] -> consume queue (Conj.agregar estado_actual visitados)
            | h :: t ->
               let nuevo_estado1 = siguiente (fst estado_actual) h (Conj.list_of_conjunto arcos1) in
               let nuevo_estado2 = siguiente (snd estado_actual) h (Conj.list_of_conjunto arcos2) in
               Queue.add (nuevo_estado1, nuevo_estado2) queue;
               procesar t
          in
          procesar (Conj.list_of_conjunto alfabeto);
  in consume queue Conj.conjunto_vacio;;
 
  • Crea un conjunto de alfabeto combinado a partir de los alfabetos de los dos autómatas usando la unión de conjuntos (Conj.union).
  • Inicializa una cola (queue) para realizar un recorrido y una estructura para llevar un registro de los estados visitados (visitados).
  • Comienza el algoritmo de recorrido mientras haya elementos en la cola:
    • Toma un par de estados actuales (uno de cada autómata) de la cola.
    • Si el par de estados actuales ya ha sido visitado, continúa con el siguiente elemento de la cola.
    • Verifica si hay diferencias entre la aceptación de estados finales en ambos autómatas. Si hay diferencias, devuelve false.
    • Si no hay diferencias, procesa cada símbolo del alfabeto:
      • Calcula el próximo estado de ambos autómatas usando la función siguiente.
      • Agrega el par de próximos estados a la cola para su posterior procesamiento.
  • Si termina el recorrido sin encontrar diferencias en los estados finales, devuelve true.

Ejemplo

let af1 = Af (
    Conjunto [Estado "0"; Estado "1"; Estado "2"], (*estados*)
 
    Conjunto [Terminal "a"; Terminal "b"], (*alfabeto*)
 
    Estado "0", (*estado inicial*)
 
    Conjunto [Arco_af (Estado "0", Estado "1", Terminal "a"); (*funcion de transicion*)
    Arco_af (Estado "1", Estado "1", Terminal "b");
    Arco_af (Estado "1", Estado "2", Terminal "a");
    Arco_af (Estado "2", Estado "2", Terminal "b")],
 
    Conjunto [Estado "2"] (*estados finales*)
  );;
 
equivalentes af1 af1;;
let af2 = Af (
    Conjunto [Estado "0"; Estado "1"; Estado "2"], (*estados*)
 
    Conjunto [Terminal "a"; Terminal "b"], (*alfabeto*)
 
    Estado "0", (*estado inicial*)
 
    Conjunto [Arco_af (Estado "0", Estado "1", Terminal "a"); (*funcion de transicion*)
    Arco_af (Estado "1", Estado "2", Terminal "b");
    Arco_af (Estado "1", Estado "2", Terminal "a");
    Arco_af (Estado "2", Estado "2", Terminal "b")],
 
    Conjunto [Estado "2"] (*estados finales*)
  );;
 
equivalentes af1 af2;;