Universidad Simón Bolívar Dpto. de Computación y Tecnología de la Información CI2661 - Laboratorio de Lenguajes I Enero-Abril 2009 Primer Proyecto: Programación lógica en Haskell 1 Programación lógica De…niciones preliminares. Asumimos inicialmente la existencia de cuatro alfabetos distintos que forman el conjunto de símbolos no-lógicos: –> Constantes: fa; b; c; :::g –> Variables: fX; Y; Z; :::g –> Funciones: ff; g; h; :::g –> Predicados: fp; q; r; :::g Además de un conjunto de símbolos lógicos: –> Conectores: f:; ^; _; ); (; ,g: –> Cuanti…cadores: f8; 9g: Cada función y cada predicado tiene asociado un número …nito n de argumentos o n-aridad. Presentamos una de…nición de la lógica de predicados usando el tipo árbol. Un término es un árbol T que satisface las siguientes condiciones: i) las hojas de T son variables o constantes, ii) todo nodo no-terminal de T es un término de la forma f ( 1 ; :::; n ) y tiene los términos 1 ; :::; n como sus n sucesores inmediatos. Si 1 ; :::; n son símbolos que designan términos y p es un predicado, entonces la fórmula atómica p( 1 ; :::; n ) es un árbol y sus n sucesores son los subárboles 1 ; :::; n : Una fórmula bien formada (o simplemente fórmula) es un árbol tal que: 1) las hojas son fórmulas atómicas, 2) un nodo no-terminal con una única fórmula sucesora es alguno entre : 8X[ (X)] 9X[ (X)] negación cuanti…cación universal cuanti…cación existencial 3) un nodo no-terminal con dos fórmulas sucesoresa ^ 2 1_ 2 1 ) 2 1 ( 2 1 () 1 2 conjunción disyunción implicación condicional equivalencia 1 1 y 2, es alguno entre Figure 1: La …gura muestra un ejemplo del árbol que representa la fórmula 9X[p(a; X; f (a; X)) ! : = (X; a)] La forma normal de cláusulas es el sublenguaje del lenguaje de la lógica de predicados en el cual se expresa la programación lógica convencional, y el lenguaje PROLOG en particular. Existe un algoritmo ([2] y [1]) que convierte cualquier fórmula en la forma normal, usando un procedimiento de reducción semántica (llamado "escolemización" en honor al lógico noruego Th. A. Skolem) que permite eliminar todos los cuanti…cadores existenciales: omitiendo la presencia de los cuanti…cadores universales, la fórmula inicial es transformada en una conjunción de disyunciones 11 .. . ^ (t11 ) _ ::: _ n1 t1ii _ : 1i1 (tn1 ) _ ::: _ nin 1i1 +1 tnin _ : t1ii +1 _ ::: _ : nin +1 1t1 t1in +1 _ ::: _ : t1 k 1 ^ ntn t n kn donde tjr es una lista de términos para todo 1 j n y 1 r nkn . Se escribe la expresión línea por línea, sin referencia explícita a las conjunciones, donde cada línea es un grupo disyuntivo aparte llamado cláusula 11 (t11 ) _ ::: _ 1i1 n1 (tn1 ) _ ::: _ nin .. . t1ii _ : 1i1 +1 tnin _ : 2 t1i1 +1 _ ::: _ : nin +1 1 k1 t1in +1 _ ::: _ : t1 k1 nk n t n kn Luego, cada cláusula , por las leyes de implicación y de De Morgan, adquiere la forma j1 (tj1 ) _ ::: _ jij tjij (= tjij +1 ^ ::: ^ jij +1 jkj tjkj para todo 1 j n. Una cláusula de Horn es una cláusula donde a lo sumo una fórmula atómica es positiva. Las cláusulas de Horn se clasi…can en: 1) Reglas; cuando una fórmula atómica es positiva y al menos una fórmula atómica es negativa j1 (tj1 ) (= j2 (tj2 ) ^ ::: ^ tjkj jkj a j1 (tj1 ) se le llama cabeza y a la conjunción j2 (tj2 ) ^ ::: ^ cuerpo. 2) Hechos; fórmulas atómicas positivas j1 (tj1 ), sin cuerpo. 3) Metas, fórmulas atómicas negativas : j1 (tj1 ) _ ::: _ : jkj jkj tjkj tjkj que se convierte en la expresión f alse ( j1 (tj1 ) ^ ::: ^ jkj tjkj Una cláusula vacía no tiene fórmulas atómicas y por de…nición no satisface ninguna interpretación, es siempre f alse. Las reglas y los hechos, que son fórmulas atómicas positivas con o sin cuerpo, son cláusulas de…nidas. Una fórmula lógica expresada como una conjunción de cláusulas de…nidas se llama programa lógico. Una fórmula lógica expresada como una cláusulas de Horn del tipo meta se llama pregunta. El lenguaje (muy reducido) de los programas lógicos y las preguntas se genera con una gramática en forma de Backus-Naur extendida del tipo: P rolog ::= AtomF orm ::= ListaT erm ::= T erm ::= F un ::= V ar ::= Const ::= T ail ::= P reg ::= fAtomF orm 0 (0 T ail 0 :0 j AtomF orm 0 :0 g 1 fa zg [0 (0 ListaT erm0 )0 ] 0 0 0 T erm f ; T ermg Const j V ar j F un 1 fa zg 0 (0 ListaT erm 0 )0 1 fA Zg 1 1 0 fa zg j f1 9g f0 9g 0 AtomF orm f0 ;0 AtomF ormg 0 (0 T ail 0 El tipo de datos en Haskell para un programa lógico a partir de esta gramática simpli…cada podría ser: 3 data I Int j B Bool j F F loat j Ch Char j St String data T erm = Const String j V ar String j F un String [T erm] data Atom = (String; [T erm]) type T ail = [Atom] data Def inite = F act Atom j Rule Atom T ail data P rogram = P rolog [Def inite] Newtype Question = Q T ail type Subs = [(T erm; T erm)] 2 V alue = Regla de inferencia lógica SLD-Resolución Una sustitución es un conjunto de remplazos de variables por términos en una fórmula atómica = fXi1 ti1 ; : : : ; X ik tik g donde X1 ; : : : ; Xn son las variables que ocurren en la fórmula. Nótese que una sustitución no afecta necesariamente todas las variables de la fórmula. La fórmula atómica ( ) que resulta de una sustitución se llama instancia de la fórmula original . Las sustituciones deben cumplir dos condiciones: 1) Funcionalidad: Xiu 6= Xiv para todo 1 u; v k con u 6= v. 2) Idempotencia: ( ( )) = ( ). Dada la fórmula y las sustituciones 1 2 = fXi1 = fYi1 t i1 ; : : : ; X ik si1 ; : : : ; Yim t ik g sim g se de…ne su composición como la sustitución 2 1 =f 2 (Xi1 ti1 ) ; : : : ; 2 (Xik tik )g[ Yij sij j Yij 2 = fXi1 ; : : : ; Xik g que se interpreta de la siguiente manera: la aplicación de 1 a introduce, eventualmente, nuevas variables que provienen de los términos ti1 ; : : : ; tik , luego, la aplicación de 2 a 1 ( ) debe darse tanto en las nuevas variables como en aquellas otras que no afecta la primera sustitución. Un uni…cador para las fórmulas 1 y 2 es una sustitución , de…nida sobre la unión de las variables de ambas, tal que ( 1) = 4 ( 2) Un uni…cador se dice general cuando, para cualquier uni…cador una sustitución tal que = existe Un uni…cador general es la mínima sustitución que iguala las instancias de ambas fórmulas y permite, eventualmente, otras sustituciones posteriores que remplacen las variables restantes. La regla de inferencia SLD-resolución usa como premisas una pregunta y una regla del programa lógico: Se instancian ambas fórmulas (la pregunta y la regla) con el uni…cador general de la primera fórmula atómica de la pregunta y la cabeza de la regla, que por supuesto, deben compartir el mismo predicado y tener la misma aridad. Formalmente, sean la pregunta (llamada en este contexto padre central) :'1 t11 ; ; t1k1 _ :'2 t21 ; ; t2k2 _ _ :'m tm1 ; ; tmkm y el programa lógico cuya primera cláusula de…nida (de allí la D, por def inida) en el orden secuencial de las cláusulas (llamada en este contexto padre lateral) es, ^ n sn1 ; ; snjn 1 s11 ; ; s1j1 ( 2 s21 ; ; s2j2 ^ cumpliendo las condiciones; i) el predicado de la primera fórmula atómica de la pregunta (de allí la S, por selectiva) es el mismo predicado de la cabeza de la cláusula de…nida, es decir, '1 = 1 , y ii) la aridad del predicado '1 es la misma que la del predicado 1 es decir, k1 = j1 , Se construye entonces la conclusión, llamada resolvente, como otra pregunta : 2 s21 ; ; s2j2 _ _: n sn1 ; ; snjn _:'2 t21 ; ; t2k2 _ _:'m tm1 ; ; tmkm que, de nuevo, será sometida al programa (de allí la L ; por lineal). Si se busca una respuesta a…rmativa cuando se somete la pregunta ' al programa , el proceso sistemático del empleo de la regla SLD-resolución debe culminar con una última un…cación cuyo resolvente es la meta cláusula vacía f alse (recuérdese que f alse es el elementro neutro del conector lógico binario _), es decir, el sistema de inferencia despliega una prueba por contradicción, por ello tiene éxito cuando se concluye f alse. Si ninguna instancia de la pregunta ' puede ser derivada lógicamente de , por medio de SLD-resolución, entonces se produce la respuesta N o. En el lenguaje de la teoría de pruebas esto es ^ :' 0 f alse 3 Algoritmo de Robinson Dadas dos fórmulas atómicas: 1 (t1 ; : : : ; tn ) y 5 2 (s1 ; : : : ; sm ) donde t1 ; : : : ; tn ; s1 ; : : : ; sm son términos (i.e. constantes, variables o funciones), el algoritmo de Robinson permite saber si ambas fórmulas atómicas son uni…cables, en cuyo caso devuelve el uni…cador general. En caso contrario, devuelve un mensaje de error razonando la falla. El algoritmo se implementa con dos pilas; la primera, , para guardar los pares de términos (ti ; si ) según aparecen, componente a componente, en las tuplas que forman los argumentos de los predicados comparados, es decir, (t1 ; s1 ) en el tope de la pila y (tn ; sn ) en el fondo; la segunda, ; para guardar las sutituciones sucesivas cuya composición constituye el uni…cador general, una entrada por variable a remplazar. Obviamente, el algoritmo falla si 1 6= 2 o n 6= m. Para facilitar la comprensión del algoritmo, categorizamos el álgebra de los términos T en tres clases; C para las constantes, V para las variables, F para las funciones. A continuación describimos informalmente el algoritmo: . Algoritmo de Robinson (in ( 1 (t1 ; : : : ; tn ) ; 2 (s1 ; : : : ; sm )) / out :: fV T g) if 1 6= 2 then mensaje de error else if n 6= m then mensaje de error else while 6= ? do pop (ti ; si ) (t; s) instancia (ti ; si ) if (t; s) 2 C C ^ t 6= s else if (t; s) = (f (: : :) ; g (: : :)) 2 F F ^ f 6= g else if (t; s) = (f (: : :) ; s) 2 F C_ (t; s) = (t; g (: : :)) 2 C F else if (t; s) = f u1 ; : : : ; unf ; g v1 ; : : : ; vng 2 F ^f = g ^ nf = ng else if (t; s) 2 V V else if (t; s) = (t; g (: : :)) 2 V F ^ occurs-check (t; s) = true else if (t; s) = (f (: : :) ; s) 2 F V occurs-check (t; s) = true else if (t; s) 2 V C [ V F else if (t; s) 2 C V [ F V end while idemp ( ) F then then then mensaje de error mensaje de error mensaje de error then then then push (u1 ; v1 ) ; : : : ; push unf ; vng push (t s) mensaje de error then mensaje de error then then push (t push (s . El algoritmo llama a tres procedimientos; 1) instancia (ti ; si ) es la función que aplica al par (ti ; si ), en cada ciclo, las sustituciones que se van acumulando en y devuelve el par actualizado (t; s). 2) occurs-check (t; s) es la función booleana que busca dentro del término t la ocurrencia de la variable s, o viceversa. 3) idemp ( ) garantiza que sea idempotente calculando el mínimo n para 6 s) t) el cual n n por lo cual 4 = n+1 será el único uni…cador general. Backtracking Todas las cláusulas de…nidas que comparten el predicado de la fórmula atómica positiva constituyen una de…nición (o procedimiento). La regla de inferencia SLD-resolución busca las soluciones (i.e. la uni…cación general) que se derivan de la uni…cación de la primera fórmula atómica con la primera cláusula de…nida en la de…nición que se corresponde con el predicado. El proceso de uni…car secuencialmente todas las cláusulas de una de…nición se llama backtracking. Posiblemente, algunas de las cláusulas produzcan soluciones positivas (uni…caciones generales) y otras no lo hagan. La estructura que describe este proceso de búsqueda de soluciones se llama SLD-árbol. 5 Ejemplo En Prolog, como en Haskell, la estrutura de datos básica es la lista, y se entiende como una función algebraica dispar ([]; list) : dispar ([]; list) ( ) = dispar ([]; list) (head; tail) = 1 j a (Lista a) ! Lista a [] list (head; tail) Sea el programa lógico c1: c2: append ([]; X; X) : append (list (A; X) ; Y; list (A; Z)) ( append (X; Y; Z) : y la pregunta ?append (list (Head; T ail1) ; T ail2; list (Head; list (a; []))) El sistema de inferencia SLD-resolución trata primero de resolver la pregunta con la cláusula c1 pero fracasa porque no puede uni…car el par de términos ([]; list (H; T 1)). Intenta luego uni…car las fórmulas atómicas append (list (Head; T ail1) ; T ail2; list (Head; list (a; []))) append (list (A; X) ; Y; list (A; Z)) devolviendo el uni…cador general 1 = fA Head; X T ail1; Y T ail2; Z El proceso de la inferencia lógica es el siguiente: 7 list (a; [])g padre central: :append (list (Head; T ail1) ; T ail2; list (Head; list (a; []))) padre lateral: append (list (Head; T ail1) ; T ail2; list (Head; list (a; []))) _ :append (T ail1; T ail2; list (a; resolvente: :append (T ail1; T ail2; list (a; [])) con lo cual el sistema debe ahora resolver la pregunta ?append (T ail1; T ail2; list (a; [])) : uni…cando las fórmulas atómicas append (T ail1; T ail2; list (a; [])) append ([]; X; X) con el uni…cador general 2 = fT ail1 []; T ail2 list (a; [])g en el proceso de refutación padre central: :append ([]; list (a; []) ; list (a; [])) padre lateral: append ([]; list (a; []) ; list (a; [])) resolvente: f alse Si se aplica backtracking, el sistema busca otra solución a partir de la última elección de padre lateral, es decir, intentará uni…car ahora las fórmulas atómicas append (T ail1; T ail2; list (a; [])) append (list (A; X) ; Y; list (A; Z)) consiguiendo el uni…cador general 3 = fT ail1 list (A; X) ; Y T ail2; A a; Z []g en el proceso de inferencia padre central: :append (list (a; X) ; T ail2; list (a; [])) padre lateral: append (list (a; X) ; T ail2; list (a; [])) _ :append (X; T ail2; []) resolvente: :append (X; T ail2; []) Finalmente, la pregunta ?append (X; T ail2; []) con el uni…cador general 4 = fX []; T ail2 se resuelve en el proceso de refutación padre central: :append ([]; []; []) padre lateral: append ([]; []; []) resolvente: f alse Las soluciones a la consulta son, primero Head =? 8 []g T ail1 = [] T ail2 = list (a; []) Luego del backtracking, Head =? T ail1 = list (a; []) T ail2 = [] Todo el proceso de inferencia se suele representar en el SLD-árbol :append (list (Head; T ail1) ; T ail2; list (Head; list (a; []))) 1 =fA 2 =fT ail1 [];T ail2 Head;X T ail1;Y T ail2;Z list(a;[])g # :append (T ail1; T ail2; list (a; [])) list(a;[])g # f alse 3 =fT ail1 list(A;X);Y T ail2;A a;Z []g # :append (X; T ail2; []) 4 =fX [];T ail2 []g # f alse Observación: Para evitar la repetición en los nombres de las variables entre el padre central y el padre lateral, el interpretador debe renombrar las variables de la cláusula del programa lógico con nombres de variables que se sepa no estan en uso. En la estructura de tipos de datos sugerida antes, tanto el programa lógico como la pregunta se expresan así: app :: Program app = Prolog [ Fact ("append",[Fun "list" [ ], Var "X", Var "X"]), Rule ("append",[Fun "list" [Var "A",Var "X"], Var "Y", Fun "list" [Var "A",Var " [("append",[Var"X",Var"Y",Var"Z"])] ] y query :: Question query = Q [ ("append", [Fun "list" [Var "H",Var "T1"], Var "T2", Fun "list" [Var "H",Fun "list" [Const (Ch ’a’),Fun "list" [ ]] ] ) ] ] 9 6 Enunciado del proyecto Se debe programar en haskell las siguientes funciones substitucion robinson resolucion backtracking : : : : : Atom ! Subs ! Atom : Atom ! Atom ! Subs : P rogram ! Question ! Subs : P rogram ! Question ! [Subs] donde Subs es el tipo de datos que implementa los uni…cadores generales type Subs = [(T erm; T erm)] Su programa deben manejar correctamente los mensajes de error cuando no sea posible uni…car dos fórmulas atómicas, o cuando SLD-resolución no pueda llegar a la cláusula vacía. Su programa debe incorporar funciones de pretty-print para presentar las fórmulas, los términos, las sustituciones y todas las soluciones del backtracking de manera legible. Documentación a entregar: El módulo P rologs:hs conteniendo el código fuente Haskell que implementa las funciones que se piden, correctamente documentado utilizando la herramienta Haddock. Un M akef ile que permita compilar el programa y generar la documentación. El programa principal será compilado y utilizado desde la línea de comandos. Fecha de Entrega. Viernes 20 de Febrero de 2009 (Semana 6). Valor de Evaluación. Treinta (30) puntos. References [1] Lloyd, J. W. (1984) Foundations of Logic Programming Springer-Verlag [2] Hogger, C. J. (1990) Essentials of Logic Programming Oxford University Press 10