Documentos de Académico
Documentos de Profesional
Documentos de Cultura
V→w
Así como cualquier gramática formal, una gramática libre de contexto puede ser definida
mediante la 4-tupla:
G = (Vt,Vn,P,S) donde
Ejemplos [editar]
Ejemplo 1 [editar]
S → aSb | ε
donde | es un o lógico y es usado para separar múltiples opciones para el mismo no
terminal, ε indica una cadena vacía. Esta gramática genera el lenguaje no regular
.
Ejemplo 2 [editar]
Aquí hay una gramática libre de contexto para expresiones enteras algebraicas
sintácticamente correctas sobre las variables x, y y z:
S → x | y | z | S + S | S - S | S *S | S/S | (S)
Ejemplo 3 [editar]
Una gramática libre de contexto para un lenguaje consistente en todas las cadenas que se
pueden formar con las letras a y b, habiendo un número diferente de una que de otra, sería:
S→U|V
U → TaU | TaT
V → TbV | TbT
T → aTbT | bTaT | ε
T genera todas las cadenas con la misma cantidad de letras a que b, U genera todas las
cadenas con más letras a, y V todas las cadenas con más letras b.
Ejemplo 4
S → aSc | B
B → bBc | ε
Otros ejemplos
El lingüista indio Pánini describió el sánscrito usando una gramática libre de contexto.
Recientemente se ha sugerido que una clase de poesía Tamil llamada Venpa utiliza
principalmente una gramática libre de contexto.
EJEMPLO DE DCG (Definite Clause Grammars) *ojo, estudien más de uno.
A definite clause grammar (DCG) is a way of expressing grammar, either for natural or
formal languages, in a logic programming language such as Prolog. DCGs are usually
associated with Prolog, but similar languages such as Mercury also include DCGs. They are
called definite clause grammars because they represent a grammar as a set of definite
clauses in first-order logic.
The term DCG refers to the specific type of expression in Prolog and other similar
languages; not all ways of expressing grammars using definite clauses are considered
DCGs. However, all of the capabilities or properties of DCGs will be the same for any
grammar that is represented with definite clauses in essentially the same way as in Prolog.
The definite clauses of a DCG can be considered a set of axioms where the validity of a
sentence, and the fact that it has a certain parse tree can be considered theorems that follow
from these axioms[1]. This has the advantage of making it so that recognition and parsing of
expressions in a language becomes a general matter of proving statements, such as
statements in a logic programming language.
Example
A basic example of DCGs helps to illustrate what they are and what they look like.
This generates sentences such as "the cat eats the bat", "a bat eats the cat". One can
generate all of the valid expressions in the language generated by this grammar at a Prolog
interpreter by typing sentence(X,[]). Similarly, one can test whether a valid sentence in
the language by typing something like sentence([the,bat,eats,the,bat],[]).
The arguments to each functor, such as (S1,S3) and (S1,S2) are difference lists;
difference lists are a way of representing a list as the difference of two lists. Using Prolog's
notation for lists, a list L can be represented with the pair ([L|X],X).
Difference lists are used to represent lists with DCGs for reasons of efficiency. It is much
more efficient to concatenate difference lists, in the circumstances that they can be used,
because the concatenation of (S1,S2) and (S2,S3) is just (S1,S3).[11]
This set of DCG rules describes the grammar which generates the language that consists of
n n n
strings of the form a b c .[12]
This grammar allows sentences like "he likes her" and "he likes him", but not "her likes he"
and "him likes him".
[edit] Parsing with DCGs
The main practical use of a DCG is to parse sentences of the given grammar, i.e. to
construct a parse tree. This can be done by providing "extra arguments" to the functors in
the DCG, like in the following rules:
One can now query the interpreter to yield a parse tree of any given sentence:
Predicados dinámicos
Una de las características más útil de Prolog es la posibilidad de añadir y eliminar cláusulas de los
predicados presentes en el programa, y hacerlo en tiempo de ejecución. Los predicados con esta posibilidad
se denominan predicados dinámicos.
Estos predicados deben ser previamente marcados mediante la directiva dynamic/1, indicando el nombre del
predicado dinámico. El siguiente ejemplo declara el predicado prueba/1 como dinámico. Por lo demás, un
predicado dinámico es como otro cualquiera excepto en que puede no tener cláusulas. En ese caso, una
llamada al predicado simplemente falla, pero no provoca un error de predicado indefinido.
:- dynamic prueba/1.
prueba(abc).
prueba(123).
Añadiendo cláusulas
Para insertar cláusulas de un predicado dinámico existe una familia de predicados ISO-standard, la familia
assert, consistente en los siguientes predicados:
La diferencia entre insertar las cláusulas por el principio o por el final es muy importante puesto que determina
el orden de sucesión de las soluciones.
El argumento que toman estos predicados es la nueva cláusula a insertar, pero teniendo en cuenta lo
siguiente:
• Las variables ligadas dentro de la cláusula se sustituyen por su valor en el momento de insertar dicha
cláusula. Esto es lo lógico y deseable.
• Las variables libres dentro de la cláusula se sustituyen por variables nuevas en el momento de la
inserción. Es decir, posteriores unificaciones no afectan a la cláusula ya insertada.
• Si existen puntos de elección para el predicado modificado (aquel para el que se inserta una nueva
cláusula), estos se mantienen y no se generan nuevos puntos de elección. Es decir, la nueva
cláusula no se tendrá en cuenta hasta que se ejecute un nuevo objetivo para el predicado en
cuestión.
Como ejemplo veamos como insertar cláusulas en el predicado prueba/1. Antes recordemos las cláusulas
que ya existen, en orden:
prueba(abc).
prueba(123).
Ahora añadimos una cláusula ejecutando asserta(prueba(666)). El programa queda como sigue:
prueba(666).
prueba(abc).
prueba(123).
De nuevo añadimos una cláusula ejecutando J=999,assertz(prueba(J)). El programa queda como sigue:
prueba(666).
prueba(abc).
prueba(123).
prueba(999).
Y para finalizar generamos una cláusula algo mas compleja mediante assertz( (prueba(X) :- X>1024)
) ...
prueba(666).
prueba(abc).
prueba(123).
prueba(999).
prueba(H) :-
H > 1024.
Eliminando cláusulas
• Los puntos de elección que ya existieren a causa de las cláusulas eliminadas permanecen mientras
sea necesario.
• retract/1 es constructivo. Las variables libres se ligan a los elementos de la cláusula eliminada.
• retract/1 tiene tantas soluciones como cláusulas existan que unifiquen con el argumento. Es decir, si
se hace backtracking sobre él, se eliminan todas las cláusulas que unifiquen. Falla cuando no hay
más cláusulas a unificar.
• retractall/1 solamente tiene una solucion, y no es constructivo. No liga las variables libres.
?- asserta(ejemplo(i(k))).
yes
?- asserta(ejemplo(i(l))).
yes
?- asserta(ejemplo(j(X))).
yes
?- assertz(ejemplo(j(2))).
yes
?- assertz(ejemplo(j(8))).
yes
?- ejemplo(X),display(ejemplo(X)),nl,fail.
ejemplo(j(_510))
ejemplo(i(l))
ejemplo(i(k))
ejemplo(j(2))
ejemplo(j(8))
no
?- retractall(ejemplo(i(X))).
yes
?- ejemplo(X),display(ejemplo(X)),nl,fail.
ejemplo(j(_510))
ejemplo(j(2))
ejemplo(j(8))
%% -- Nota --
%% Aqui provocamos el backtracking pidiendo
%% otra solucion.
no
?- retract(ejemplo(j(X))).
yes
?- retract(ejemplo(j(X))).
X = 2 ? ;
X = 8 ? ;
no
?-
El lector se preguntará qué finalidad tiene la propiedad tan espantosa de modificar el código. Haciendo un mal
uso de los predicados dinámicos solamente provocamos ilegibilidad del programa, dificultad para realizar
trazas, efectos laterales y otras desgracias. El único uso legítimo es la implementación de estado en los
programas Prolog. Supongamos que necesitamos un programa que controle la temperatura de una sala:
:- dynamic temperatura/1.
temperatura(23).
sensor_temperatura :-
esperar(10),
leer_temperatura(NuevaTemperatura),
rectract(temperatura(_AnteriorTemperatura)),
assert(temperatura(NuevaTemperatura)),
!,
sensor_temperatura.
Sin assert/retract no sería posible almacenar el estado del sensor de temperatura. En general, solamente
deberían ser predicados dinámicos aquellos que almacenan hechos simples, es decir, aquellos cuyas
cláusulas no tienen cuerpo.
Ejemplo
El siguiente ejemplo implementa una pila de datos al estilo imperativo mediante assert/retract.
:- module(pila,[],[]).
:- export(push/1).
:- export(pop/1).
:- dynamic datos/1.
push(NuevoDato) :-
asserta(datos(NuevoDato)).
pop(Dato) :-
retract(datos(Dato)),
!.
Para implementar una cola en lugar de una pila bastaría con sustituir asserta por assertz.
Ya hemos mencionado anteriormente el efecto de los predicados assert/retract sobre los puntos de elección
en el programa, sin embargo, vamos a insistir un poco más sobre esto.
Decimos que un programa tiene coherencia lógica cuando hace exactamente lo que dice el programa escrito.
Pero los predicados dinámicos podrían, en principio, provocar comportamientos inexplicables en un programa.
Veamos un ejemplo, el siguiente programa consiste en un bucle de fallo. Lo que dice el programa es que se
hace backtracking sobre el predicado test/1, que tiene un número finito de soluciones. Por tanto es un
programa finito. Hemos numerado las lineas del programa que nos interesan.
test(sol1).
test(sol2).
main :-
test(X), %% Linea 1
display(X), nl, %% Linea 2
asserta(test(sol)), %% Linea 3
fail. %% Linea 4
Ahora supongamos que asserta/1 no respetase los puntos de elección. Ocurriría lo siguiente:
El programa resulta infinito porque asserta/1 no para de insertar puntos de elección. Pero eso no es lo que
dice el programa.
Afortunadamente, assert/retract no modifican los puntos de elección hasta que no se han eliminado todos los
que existieran previamente del predicado afectado. Por eso, la ejecución de main/1 llevaría al siguiente
resultado:
?- main.
sol1
sol2
yes
?-
?- main.
sol
sol1
sol2
yes
?-
Esto es lo que se denomina mantener la coherencia lógica del programa. Es decir, no se permite que un
programa se modifique a sí mismo hasta que no termine de ejecutarse la parte afectada.
Interfaz de usuario
El mecanismo que permite la comunicación entre el usuario y el sistema experto
Medio de explicación
Explica al usuario el razonamiento del sistema. Es el que permite justificar y
explicar el análisis completo del problema y las soluciones propuestas, así como la
semejanza o diferencia entre dicha solución y las de los casos históricos.
Memoria activa:
Una base de datos global de los hechos usados por las reglas
Mecanismo de inferencia:
Hace inferencias al decidir cuales reglas satisfacen los hechos u objetos, da
prioridad a las reglas satisfechas y ejecuta la regla con la prioridad más elevada
Agenda
Una lista con prioridades asignadas a las reglas, creada por el mecanismo de
inferencia, cuyos patrones satisfacen los hechos u objetos de la memoria activa. La pila de
objetivos en la resolución lineal
Medio para la adquisición de conocimiento
Vía automática para que el usuario introduzca conocimientos en el sistema, sin
tener al ingeniero del conocimiento para qué codifique este en forma explícita. El
módulo de adquisición del conocimiento permite que se puedan añadir, eliminar o
modificar elementos de conocimiento (en la mayoría de los casos reglas) en el sistema
experto. Si el entorno es dinámico es muy necesario, puesto que, el sistema
funcionará correctamente sólo si se mantiene actualizado su conocimiento. El
módulo de adquisición permite efectuar ese mantenimiento, anotando en la base de
conocimientos los cambios que se producen [CHAP].
Todos los conocimientos que se obtienen deben ser estructurados de una forma correcta,
todo este conocimiento se almacena en lo que se conoce como la base de conocimientos
OJO: LES ANEXO UNA PRÁCTICA CON UN SISTEMA EXPERTO PARA QUE LO
CAPTUREN EN EL SWI PROLOG Y LO CORRAN, VEAN COMO FUNCIONA.
4.- ÁRBOL SLD.
SLD (Selection Linear Definite)
Aplicar la regla de Selección de cláusulas (el orden de las cláusulas es de arriba hacia abajo,
o sea como están en la base interna de predicados de prolog y la selección de objetivos es
de izquierda a derecha), posteriormente la resolución Lineal (que vimos en clase) sobre un
conjunto de cláusulas Definidas (las que forman los programas).
La idea entonces, es aplicar la resolución lineal que vimos en clase y graficarla como árbol.
Ejemplos:
Cláusulas Definidas (o sea el programa)
p(X,Z):- q(X,Y), p(Y,Z).
p(X,X).
q(a,b).
Objetivo (goal)
?- p(X,b).
Ejemplo 2
Cláusulas definidas:
alumno(A,P):- estudia(A,C), enseña(P,C).
estudia(ana, ia).
estudia(ana,pl).
estudia(eva,ra).
enseña(jose_a, ia).
enseña(jose_a, ra).
enseña(Rafael,pl).
Consulta (goal).
?- alumno(A, jose_a).
ARBOL SLD.
NOTAS:
-LAS HOJAS REPRESENTAN CASOS DE ÉXITO Y FALLO, EN CASO DE ÉXITO LA
HOJA DEBERÁ TENER EL UMG CON ELCUAL LA CONSULTA SE HACE VERDADERA.
-ES COMO EJECUTAR EL GOAL (OBJETIVO) DE MANERA EXHAUSTIVA, COMO SI
DESPUES DE LA PRIMERA RESPUESTA, PREGUNTÁRAMOS OTRA VEZ CON “;” Y
VOLVIÉSEMOS A APLICAR EL MECANISMO DE RESOLUCIÓN LINEAL CON
UNIFICACIÓN QUE VIMOS EN CLASE.
5.- Ordenamiento de listas.
Estudien los algoritmos que dejamos en clase, más el mergesort y burbuja mejorado.
6.- Recuerden que en prolog las estructuras de control para realizar iteración (que son
parte esencial de los lenguajes estructurados) no existen, en su lugar obligamos a la
máquina de inferencias a iterar por medio de los predicados de tipo……?