El mundo de bloques en
lenguaje natural
Borja Buera y Mapi Torres
Lenguaje natural
 Es el lenguaje que se habla comúnmente.
 El objetivo de las interfaces en lenguaje natural es hacer la
interacción hombre máquina lo más intuitiva posible,
llegando a poder conversar con la aplicación.
 El reconocimiento de todo un idioma es muy complicado
(imposible).
 Por ello, la gramática se encarga de reconocer un
subconjunto del idioma, el subconjunto necesario según el
tipo de aplicación que sea.
 Presenta el problema de la reutilización: Cambio de idioma
o aplicación -> rehacer la gramática completa.
Lenguaje natural
 Usuario debe saber qué puede pedir y exigir al programa,
conocerlo bien.
 Son útiles las ayudas, donde se puede explicar cómo sacar
partido al lenguaje.
 Hacer gramáticas correctas es especialmente complicado en
algunos idiomas como el español, por la diversidad de
tiempos, formas verbales, géneros, etc.
 Aunque la necesidad de reconocer frases correctas no
siempre es esencial.
Lenguaje natural
 Nuestra aplicación, que se explicará con detalle más
adelante, reconoce la frase:
pon la bloque A sobre el mesa en el posicion 2
 Pero se sabe lo que se quiere decir, y que las frases sean
correctas gramaticalmente o no, no afecta para nada al
mundo de bloques.
 Explicaremos ahora algo sobre el interprete de lenguaje
natural utilizado.
DYPAR-I
 Dypar-I es un intérprete de lenguaje natural.
 Su objetivo principal es servir como
herramienta de programación para escribir
gramáticas para una aplicación específica.
 Está implementado en FRANZ LISP.
Objetivos de las interfaces de lenguaje
Natural
 El principal: dar suficiente cobertura a aquellos
aspectos del lenguaje natural utilizados por la
aplicación en cuestión (frente a cubrir todo el
lenguaje natural).
 Además: buenas prestaciones, portabilidad a otros
dominios, gramática extensible.
 DYPAR-I cumple estos objetivos y algunos de
forma más eficiente que sistemas previos.
El mundo de bloques
Enunciado:
Se va a construir la gramática para el mundo de bloques,
pudiendo ser bloques triángulos, círculos, rectángulos o
cuadrados.
Un bloque cualquiera podrá colocarse sobre la mesa, un
cuadrado o un rectángulo, siempre que no se salga de éste.
Los bloques pueden moverse a derecha a izquierda, si hay
espacio y no se caen del bloque que los sustenta.
Se han escrito funciones que dan información acerca de qué o
cuantos bloques hay encima o debajo de otro.
El mundo de bloques
La longitud de la mesa se considera de 10 unidades.
No se permitirá colocar un bloque encima de un círculo o
triángulo.
Para ver la situación de los bloques, los movimientos se irán
reflejando en una ventana.
Las funciones necesarias para nuestra aplicación han sido
escritas en LISP, siguiendo un modelo basado en frames.
Para utilizarlas, se realizan llamadas desde el fichero de la
gramática.
El mundo de bloques
 La ventana es nuestro
elemento visualizador.
 Para su implementación
nos hemos ayudado del
paquete COMMONGRAPHICS.
 Cada vez que añadamos o
quitemos algo, la ventana
se irá actualizando.
El mundo de bloques
Otra funcionalidad: planificación
Ante una petición del usuario del tipo:
planifica el bloque A sobre el bloque B
el sistema realiza los movimientos adecuados para mover
un bloque encima del otro (retirada, movimiento de otros
bloques etc).
Pattern matching
 Cada regla de DIPAR consiste en un patrón y una
acción. Si el patrón es reconocido se ejecuta la
acción.
 Los patrones son listas compuestas de símbolos
terminales (números, palabras y símbolos de
puntuación), operadores y otros patrones.
Operadores básicos
Lo primero a hacer en nuestra aplicación es definir
instancias de bloques. Para ello se puede poner:
El bloque A es una instancia de cuadrado.
Para reconocer exactamente esa frase en nuestra
gramática deberíamos crear un patrón simple con ella:
(El bloque A es una instancia de cuadrado)
Es decir, debe ir entre paréntesis, y estar compuesto
por terminales.
Operadores básicos
Sin embargo, podría decirse que el bloque A es un ejemplar
de cuadrado, en vez de una instancia. Necesitamos crear
conjuntos de palabras con la misma funcionalidad o
significado. Para ello utilizamos el operador or:
(instancia | ejemplar | individuo)
(un | uno | una | unos | unas)
Y el bloque puede llamase de cualquier manera. Para
reconocer cualquier cosa, tenemos el operador $ (más
adelante se verá un ejemplo de uso).
Operadores básicos
Otro operador importante es la asignación, sobre todo en
los nombres:
(el bloque (nombre := $) es un ejemplar de cuadrado)
Así se recuerda un troza de entrada.
Otro uso interesante del operador $ es cuando una frase
puede ponerse más o menos completa. Por ejemplo, al
poner un bloque sobre la mesa podemos escribir:
Pon el bloque A sobre la mesa
Operadores básicos
Pon el bloque A sobre la mesa en la posición 2
Se harán dos patrones de reconocimiento, el primero más
específico, que se guarda la posición y coloca allí el bloque
si es posible:
(<verbos-poner> <det> bloque <sobre> la mesa en la posicion (!num := $)>
Y una segunda:
(<verbos-poner> <det> bloque <sobre> la mesa $)
Que en el caso de equivocarse, se traga toda la parte de la
posición.
Operadores básicos
Por último el $ se ha utilizado para que la gramática no de un
error en caso de no reconocer la frase. Con (* $) en último
lugar, reconoce cualquier frase no reconocida previamente.
¿qué pasa si queremos poner solamente
A es un ejemplar de cuadrado?
Para hacer opcional un bloque de palabras, tenemos el
operador ?
Podría ponerse:
(?(el bloque) (nombre := $) es un ejemplar de cuadrado)
Reglas en DYPAR
Las Rewrite Rules definen los bloques con los cuales
se construyen otras reglas. Asocian clases de palabras
con un símbolo.
¿ Que hacer con el conjunto (instancia | ejemplar |
cuadrado)?
<ejemplar> -> (instancia | ejemplar | individuo)
Y con otros:
<articulo> -> (el | la | los | las)
<ser-presente> -> (es | son)
Reglas en DYPAR
<qqc> -> (que | quien | cual)
También pueden englobarse sinónimos:
<verbos-medir> -> (tiene | es | mide)
Y así, la frase inicial queda:
(?(<det> bloque) (nombre := $) es <det> <ejemplar> de cuadrado)
Dypar no reconoce no-terminales a menos que estén
encerrados entre <>
El símbolo -> sirve para identificar este tipo de reglas.
Reglas en DYPAR
Las reglas top-level tienen tanto el patrón a reconocer
como las instrucciones a ejecutar si el patrón se
reconoce.
Es decir:
(Patrón a reconocer)
=>
(Instrucciones a ejecutar, programadas en LISP)
Reglas en DYPAR
Ejemplo:
Reconocer el patrón “A es un ejemplar de cuadrado”,
e inicializar sus parámetros:
( (!nombre := $) <ser-presente> <un> <ejemplar> de (!valor := $) )
=>
(progn (form :name (first !nombre) :is-a (first !valor) )
(set-value (first !nombre) 'ejemplar 'si)
(set-value (first !nombre) 'colocado 'no)
(msg "Vale, " (first !nombre) " es un(a) ejemplar de " (first !valor))
)
Reglas en DYPAR
Las reglas de transformación sirven para transformar
ciertas construcciones lingüísticas en otras más comunes.
En estas situaciones, el código a ejecutar es el mismo.
Transformando, no se duplica código.
En inglés hay mas ejemplos que en español:
Sally’s mother
 the mother of Sally
El símbolo que da a reconocer esta regla es ::>
En nuestra aplicación no han sido utilizadas.
Otros operadores
Existen otros operadores en DYPAR:
+: Reconoce una o más veces un patrón;
=: Referencia el valor de una variable;
&u: consume entrada hasta que encuentra un
determinado patrón, sin incluirlo.
&c: se utiliza cuando varios patrones pueden aparecer en
distinto orden e interesa reconocer todos.
Construyendo el sistema
 A continuación veremos los ficheros
necesarios para construir un sistema:
 Gramatica.gra
 Bloques.lsp y ventana.lsp
 Fichero de carga
 Fichero de ruta
 Otros ficheros
Gramática
El fichero gramatica.gra contiene la gramática, es decir, las
reglas rewrite, las top-level y las de transformación.
Dentro de las reglas top-level hay llamadas a funciones
(propias del mundo de bloques o de escritura en la ventana)
que no están en este fichero.
Gramática
Veamos algunas rewrite rules:
<ser-presente> -> (es | son)
<ser> -> (<ser-presente>)
<part-interrogativas> -> (que | donde | cuantos | cuanto )
<qqc> -> (que | quien | cual)
<que-es> -> (<qqc> <ser-presente>)
<verbos-educacion-tu> -> ( podrias | puedes )
<verbos-educacion-usted> -> (podria | puede)
<educacion> -> (<verbos-educacion-tu> ?tu | <verbos-educacion-usted> ?usted )
<info-req1> -> (<educacion> <info-req2> ?<que-es>)
<info-req2> -> (decirme | darme | imprimir | escribir )
<info-req4> -> ((dime | escribe | dame | imprime) <que-es>)
<info-req3> -> (<qqc> | <polite> <info-req2> ?<qqc>)
<info-req5> -> (<qqc> | ((dime | escribe | dame | imprime) ?<qqc>))
<info-req> -> (<que-es> | <info-req1> | <info-req4> )
Gramática
Reconocimiento de un bloque:
( <info-req> ?<det> (!nam :=$) ?<punct>)
=>
(msg (first !nam) " es un(a) " (first (what-is-it !nam)))
Gramática
Aserciones:
(?<un> (!nombre := $) <ser-presente> <un> <ejemplar> de (!valor := $) ?<dpunct>)
=>
(if (eq (get-value (first !valor) 'ejemplar) 'si)
(msg "Error, " (first !valor) " es un ejemplar, y no puede haber ejemplar de
ejemplar")
(progn (form :name (first !nombre) :is-a (first !valor) )
(set-value (first !nombre) 'ejemplar 'si)
(set-value (first !nombre) 'colocado 'no)
(msg "Vale, " (first !nombre) " es un(a) ejemplar de " (first !valor)))
)
Gramática
Dar valor al radio del círculo:
(el radio de (!nombre := $) <verbos-medir> (!num := $))
=>
(if (is-a !nombre (list 'circulo))
(if (eq (get-value (first !nombre) 'colocado) 'si)
(msg "No puede variar el radio si el circulo esta sobre la mesa")
(progn (set-value (first !nombre) 'dimension (list (* '2 (first !num)) (* '2 (first !num))))
(msg "Vale, El radio de " (first !nombre) " es " (first !num) )))
(msg "Error, " (first !nombre) " no es un circulo "))
Gramática
(<verbos-poner> ?(<el> bloque) (!nombre := $) sobre la mesa en <el> <lugar> (!num := $))
=>
(if (eq (hay-espacio (first (get-value (first !nombre) 'dimension))
(second (get-value (first !nombre) 'dimension))
(list (first !num) 0) ) 0)
(msg "Error. No hay espacio")
(if (eq (get-value (first !nombre) 'colocado) 'si)
(msg "Error " (first !nombre) " ya esta colocado")
(if (eq (se-sale-de-mesa (first !nombre) (first !num)) 'si)
(msg "Error. El bloque " (first !nombre) " no esta dentro de las dimensiones d ela mesa
")
Gramática
(progn (set-value (first !nombre) 'posicion (list (first !num) 0))
(set-value (first !nombre) 'tiene-debajo (list 'mesa))
(set-value (first !nombre) 'tiene-encima nil)
(añadir-a-mesa (first !nombre))
(set-value (first !nombre) 'colocado 'si)
(if (is-a !nombre (list 'circulo))
(pintar-circulo (first !nombre))
(if (is-a !nombre (list 'triangulo))
(pintar-triangulo (first !nombre))
(pintar-elemento (first !nombre))))
(actualizar-matriz-espacial (first (get-value (first !nombre) 'dimension))
(second (get-value (first !nombre) 'dimension))
(get-value (first !nombre) 'posicion) ))))
)
Fichero de funciones propias
El fichero se llama bloques.lsp y contiene:
-Las estructuras de datos necesarias para guardar la
información de los bloques (radio, lado, color, etc)
-Las funciones para interaccionar con ellos (moverderecha, hay-espacio, bloques-totales-debajo...)
Funciones de ventana
Contiene las funciones propias de la ventana: crear-bloque,
pintar-bloque, crear-mesa ...
(defun pintar-elemento(obj)
;; obj es el nombre del bloque a pintar con propiedades
;; posicion, (posx posy) y dimension, un entero
(setf x (+ *inicio-ventana-horizontal*
(* *pixel-por-unidad* (first (get-value obj 'posicion)))))
(setf y (- *inicio-ventana-vertical*
(* *pixel-por-unidad* (second (get-value obj 'posicion)))))
(set-value obj 'dibujo (COMMON-GRAPHICS:make-box x y
(+ x (* *pixel-por-unidad* (first (get-value obj 'dimension))))
(- y (* *pixel-por-unidad* (second (get-value obj 'dimension))))))
(COMMON-GRAPHICS:draw-box mi-ventana (get-value obj 'dibujo)))
Ficheros de funciones genéricas
Existen otras funciones necesarias y que no son exclusivas
del mundo de bloques: is-a, set-value, get-value...
Estas funciones están en un conjunto aparte de ficheros.
Todos los ficheros necesarios tienen que cargarse. Para
ello se utiliza (load “fichero-en-cuestion.lsp”)
Es una buena idea crear un fichero que automáticamente te
cargue todos los ficheros necesarios y la gramática. En
nuestro caso, este fichero se llama boot-ierl-dypar.lsp
El usuario
Cuando el usuario escribe, Gramatica.gra
escribe un conjunto de palabras
que será reconocido (si es
posible) por la gramática.
Interacciones entre ficheros
Ventana.lsp
Otros ficheros
Bloques.lsp
Gramatica.gra
Puesta en marcha del sistema
Veamos ahora los pasos necesarios para instalar el
sistema.
Conviene hacerse un fichero ruta.txt con las rutas de
las fuentes. En nuestro caso:
:cd C:/isbc/fuentes/Ierl+dypar
:ld boot_ierl_dypar.lsp
La primera linea es la ruta de los ficheros fuentes.
Puesta en marcha del sistema
La segunda es el load del fichero que a su vez,
contiene todos los load necesarios.
En el allegro Common Lisp deberemos reconocer estas
dos líneas. Si el fichero boot-ierl-dypar carga la
gramática, ya puede empezar a utilizarse.
Descargar

DYPAR-I