VP - TEMA 2

94
VISUAL PROLOG Entorno de desarrollo 1 Secciones de un programa en Visual Prolog 1.1 Directivas de compilación 1.2 Sección de constantes 1.3 Sección de dominios 1.3.1 Dominios de Objetos Compuestos 1.3.2 Dominios de Objetos Compuestos Functorless 1.3.3 Sinónimos de Dominios Estándar 1.3.4 Dominios tipo Lista 1.3.5 Dominios tipo Predicado 1.4 Sección de la base de datos 1.4.1 Predicados definidos en Visual Prolog para manejar la base de datos 1.5 Sección de definición de predicados 1.5.1 Tipos de predicados 1.5.2 Ejemplos de cómo conseguir la coherencia entre reglas y predicados en el aspecto determinista 1.6 Sección de cláusulas 1.7 Sección de meta u objetivo 1.8 Ejemplo

description

Sistemas expertos

Transcript of VP - TEMA 2

Page 1: VP - TEMA 2

VISUAL PROLOGEntorno de desarrollo

 

1 Secciones de un programa en Visual Prolog

1.1 Directivas de compilación

1.2 Sección de constantes

1.3 Sección de dominios

1.3.1 Dominios de Objetos Compuestos

1.3.2 Dominios de Objetos Compuestos Functorless

1.3.3 Sinónimos de Dominios Estándar

1.3.4 Dominios tipo Lista

1.3.5 Dominios tipo Predicado

1.4 Sección de la base de datos

1.4.1 Predicados definidos en Visual Prolog para manejar la base de datos

1.5 Sección de definición de predicados

1.5.1 Tipos de predicados

1.5.2 Ejemplos de cómo conseguir la coherencia entre reglas y predicados en el aspecto determinista

1.6 Sección de cláusulas

1.7 Sección de meta u objetivo

1.8 Ejemplo

2 El entorno de desarrollo de Visual Prolog

2.1 Aplicaciones orientadas a eventos

Page 2: VP - TEMA 2

2.2 Aplicaciones orientadas a eventos en Visual Prolog

2.2.1 Tipos de ventanas en Visual Prolog

2.2.2 El diálogo Experto del entorno de desarrollo VPI

2.2.3 El primer programa VPI

 

Secciones de un programa en Visual PrologUn programa en Visual Prolog está compuesto de varias secciones que se describen a continuación:

Directivas de compilación: que se dan al comienzo del programa. Sección de constantes: puede contener cero, una o varias constantes. Sección de dominios: puede contener cero, uno o varios dominios. Sección de la base de datos: puede contener cero, uno o varios

predicados de la base de datos. Sección de predicados: puede contener cero, una o varias declaraciones

de predicados. Sección de cláusulas: puede contener cero, una o varias cláusulas. Sección de meta: para que el programa se ejecute de forma

independiente debe existir una meta construida dentro del propio programa.

[ÍNDICE]

Directivas de compilación

Nos permiten controlar las características del compilador. Estas directivas pueden ser cambiadas a través del menú, mediante opciones de línea de comando y en el propio código fuente. En caso de activar una misma directiva de forma distinta en el entorno y en el código fuente, prevalece la activación realizada en el código fuente. Ver Tabla 1

Directiva Significado

bgidriverEsta directiva se utiliza de un modo similar a este ejemplo: bgidriver "_CGA_driver_far", para establecer el tipo de controlador de gráficos que se debe linkar con un programa MS-DOS de gráficos BGI.

Page 3: VP - TEMA 2

bgifont

Esta directiva se utiliza de un modo similar a este ejemplo: bgifont "_gothic_font_far", para establecer el tipo de fuente de texto que se debe utilizar con el programa a la hora de escribir un programa para MS-DOS basado en gráficos BGI.

check_determ

Esta directiva activa la detección de cláusulas no deterministas. Si especificamos esta directiva, Visual Prolog dará un warning cada vez que se detecte un predicado de tipo no determinista. Hay dos tipos de cláusulas no deterministas:

Aquellas que no poseen corte y hay una o más cláusulas que pueden casar con los mismos argumentos de entrada para un patrón de flujo.

Aquellas que llaman a otras no deterministas y el predicado llamado no está seguido de un corte.

code

Especifica el tamaño del array interno de código. Por defecto son 2000 párrafos (unidades de 16 bits), para la versión VP de 16 bits. En otro caso, son 10000. Por ejemplo si escribimos code=1024 tenemos un array de código de 16 * 1024 bytes=16Kbytes de tamaño.

config Permite definir el aspecto de los programas en modo texto en MS-DOS antiguo.

diagnostics Permite observar los un conjunto de parámetros de un programa Prolog.

error

Produce mensajes de error en tiempo de compilación. Cuando se encuentra un error de este tipo al llegar a la línea de la directiva, se visualiza en pantalla el mensaje escrito al lado de #error. Es muy útil para encontrar inconsistencias debidas al programador y violación de restricciones impuestas.

errorlevel Permite establecer el grado de detalle con que VP informa de los errores al programador. Puede ser 0, 1 ó 2.

heap Sirve para especificar la cantidad de memoria que se reservará para un ejecutable TSR en MS-DOS.

gstacksizeSe debe escribir en la sección MAIN para que sea tenida en cuenta y permite establecer el número de bytes de la pila global (cantidad de memoria virtual que el sistema operativo debe reservar), mientras el programa está cargado.

nobreak

En ausencia de esta directiva cada vez que se realice una llamada a un predicado, se examinará el teclado para ver si se ha pulsado ctrl-break, por lo que se ralentiza la ejecución de la aplicación y, además, ocupa un pequeño espacio de programa. El uso de la directiva evita que se genere este código examinador. Cuando se utiliza esta directiva, la única forma de salir de un bucle infinito es mediante un proceso de reset.

nowarningsSuprime los avisos generados por la existencia de una variable una sola vez en una cláusula. Estas variables pueden ser sustituidas por variables anónimas, luego, en principio, no deben aparecer en un programa bien escrito.

printermenu Permite la generación de código en programas MS-DOS que facilita la impresión de pantallas en modo texto, mediante la pulsación del conjunto Alt-p

project Se usa cuando utilizamos una metodología de diseño e implementación modular. Permite especificar a qué proyecto pertenece un módulo determinado.

Tabla 1

[ÍNDICE]

Page 4: VP - TEMA 2

Sección de constantes

En la sección de constantes podemos declarar constantes simbólicas que pueden usarse en el cuerpo del programa. La utilidad de las constantes en Visual Prolog es similar a la que estos elementos tienen en otros lenguajes de programación, normalmente, sirven para facilitar el uso de diversas cantidades, expresiones y símbolos.

La definición de la sección de constantes se encabeza con la palabra reservada CONSTANTS, y cada línea de definición es de la forma <Nombre Constante> = <Definición de Macro>.

Por ejemplo, el siguiente fragmento de programa visualiza en pantalla el contenido:

12

A=1, B=2

1 Solution

CONSTANTS

numero = 1

expresion = 1+1

GOAL

A=numero, B=expresion, write(A), write(B), nl.

Las restricciones que se imponen en el uso de constantes se exponen a continuación:

El sistema no distingue entre mayúsculas y minúsculas. La definición de una constante no puede referirse a sí misma, es decir, no

es posible construir definiciones recursivas de constantes. Puede haber varias secciones CONSTANTS a lo largo del programa, y toda

constante debe ser definida antes de ser utilizada. Las constantes declaradas son efectivas desde el punto en que son

declaradas hasta el final del fichero fuente donde han sido definidas y en algunos ficheros incluidos tras la declaración.

Las constantes sólo se pueden declarar una vez.

[ÍNDICE]

Page 5: VP - TEMA 2

Sección de dominios

En la sección DOMAINS se pueden definir dominios no especificados por defecto por Visual Prolog. Por ejemplo, el dominio integer es estándar para el compilador, no así el dominio sinónimo ENTERO o dominios complejos. En estos últimos casos, el programador debe definirlo en esta sección.

Cada sección de dominios debe comenzar con la palabra DOMAINS. Ejemplo:

DOMAINS

ENTERO = INTEGER

Es posible usar 5 formatos de declaraciones para dominios definidos por el usuario.

[ÍNDICE]

Dominios de Objetos Compuestos

El formato es como sigue:

dominio = [reference] [align {byte|word|dword}] declaracion1;[declaracion2];...

dominio: nombre dado al dominio especificado por el usuario. declaracion1; [declaracion2]: declaración de objetos compuestos

alternativos que tienen que tener la forma siguiente: functor([sub_1 [, sub_2, ...] ]) donde

o functor es el nombre del objeto compuesto alternativo.o sub_1 [,sub_2, ...] son subcomponentes del objeto compuesto que

deben tener su correspondiente nombre y dominio. Este último puede ser estándar o definido por el usuario.

reference: indica la declaración de un dominio de referencia. En este tipo de dominios los argumentos de entrada pueden ser variables libres.

align: indica el tipo de distribución de memoria que se va a usar.

Ejemplo:

DOMAINS

LECTOR = lee(SYMBOL Nombre, LECTURA Item)

LECTURA = libro(SYMBOL Autor, SYMBOL Titulo, SYMBOL Editorial);

Page 6: VP - TEMA 2

revista (SYMBOL Titulo, INTEGER Numero)

Los nombres colocados al lado de cada dominio en los subcomponentes de la definición de los dominios del ejemplo son opcionales y pueden omitirse, ya que sólo sirven para aumentar la legibilidad del programa.

El símbolo ";" indica que el dominio LECTURA puede referirse tanto a libros como a revistas, es decir, tanto los objetos libros como los objetos revistas pertenecen al dominio LECTURA.

El siguiente programa lista lo que cada lector lee en las variables X e Y respectivamente.

DOMAINS

LECTOR = lee(SYMBOL Nombre, LECTURA Item)

LECTURA = libro(SYMBOL Autor, SYMBOL Titulo, SYMBOL Editorial);

revista (SYMBOL Titulo, INTEGER Numero)

PREDICATES

lectores(LECTOR)

CLAUSES

lectores(lee(antonio, libro(cervantes, quijote, anaya))).

lectores(lee(pepe, revista(hola, 22))).

lectores(lee(juan, libro(delphi4, alvarez, anaya))).

GOAL

lectores(lee(X,Y)).

El resultado obtenido tras la ejecución del objetivo propuesto en la sección de metas es:

X=antonio, Y=libro("cervantes","quijote","anaya")

X=pepe, Y=revista("hola",22)

X=juan, Y=libro("delphi4","alvarez","anaya")

3 Solutions

[ÍNDICE]

Dominios de Objetos Compuestos Functorless

Page 7: VP - TEMA 2

En estos dominios podemos definir tipos de objetos que sólo se pueden comportar como una única clase de objetos. Mientras que en el caso anterior un determinado dominio podía definir un objeto u otro, en el caso de dominios de objetos Functorless dicho dominio sólo puede definir un objeto compuesto. La sintaxis de este tipo de dominios es similar a la anterior salvo porque es necesario especificar la palabrastruct y no puede haber más de una definición de objeto por dominio.

dominio = struct [align {byte|word|dword}] declaracion

Con este tipo de declaración los objetos utilizado mantienen una sintaxis muy parecida a la de C.

Ejemplo:

DOMAINS

ESCULT = struct escultura (INTEGER ns, SYMBOL autor, SYMBOL material)

PINTURA = struct cuadro (INTEGER ns, SYMBOL autor, SYMBOL tecnica)

[ÍNDICE]

Sinónimos de Dominios Estándar

En ocasiones, puede ser útil definir sinónimos de dominios que son estándar, para una mayor legibilidad del programa, por ejemplo. Así pues en la siguiente declaración:

DOMAINS

ENTERO = integer

ESCULT = struct escultura (ENTERO ns, SYMBOL autor, SYMBOL material)

PINTURA = struct cuadro (ENTERO ns, SYMBOL autor, SYMBOL tecnica)

ENTERO no es más que un sinónimo de integer que puede ser usado del mismo modo que este último.

[ÍNDICE]

Dominios tipo Lista

Las listas son estructuras comunes de datos en Prolog consideradas como una forma de objeto compuesto. Son secuencias de elementos generalmente

Page 8: VP - TEMA 2

homogéneos. Sin embargo, en Visual Prolog es fácil crear listas heterogéneas mediante el uso eficaz de declaración de dominios.

La sintaxis para crear una lista de objetos es del modo:

dominiolista = dominiocomponentes*

Si dominiocomponentes define varios objetos distintos, la lista definida será heterogénea o polimórfica.

Veamos varios ejemplo de listas homogéneas:

DOMAINS

listaenteros = integer* /* Lista de enteros */

listacaracteres = char* /* Lista de caracteres */

listacuadros = cuadro* /* Lista de estructuras del dominio cuadro */

Veamos ahora un ejemplo de lista polimórfica que puede almacenar tanto estructuras tipo cuadro como estructuras tipo escultura:

DOMAINS

objetos = escultura (ENTERO ns, SYMBOL autor, SYMBOL material);

cuadro (ENTERO ns, SYMBOL autor, SYMBOL tecnica)

/* Los objetos pueden ser esculturas o cuadros */

listapolim = objetos* /* Esto es una lista de objetos */

[ÍNDICE]

Dominios tipo Predicado

Un dominio predicado (dominio puntero a predicado), permite la declaración de un grupo o una clase de predicados. La sintaxis es como sigue:

PredicadoDom = TipoPredicado [Retorno] (ListaArg) [- [[TipoPredicado] patron]] [lenguaje]

donde:

PredicadoDom es el nombre del dominio.

Page 9: VP - TEMA 2

TipoPredicado es de la forma {procedure | determ | nondeterm | failure | erroneous | multi}, estos son los tipos de predicados que pueden existir.

Retorno es el dominio al que pertenecen los datos de retorno cuando el predicado es una función.

ListaArg es la lista de argumentos del predicado que son objetos con sus correspondientes dominios.

patron (de flujo) define como se van a usar los argumentos, si son de entrada (i), o de salida (o), o de entrada-salida (i|o).

lenguaje define el tipo de lenguaje que ha sido usado para definir un tipo de predicado. Puede ser: language{ asm | c | pascal | prolog | stdcall | syscall }.

Ejemplo:

DOMAINS

par = pares (INTEGER, INTEGER)

listapares = par*

listaenteros = integer*

unaria = determ INTEGER (INTEGER) - (i)

PREDICATES

predicadosuma(par, INTEGER)

cubo: unaria

cuadrado: unaria

operacion(listapares, unaria, listaenteros)

CLAUSES

cubo(E, ECubo):- ECubo=E*E*E.

cuadrado(E, ECuadrado):- ECuadrado=E*E.

predicadosuma(pares(E1, E2), Suma):- Suma=E1+E2.

/*---*/

operacion([],_,[]).

operacion([X|Y], OperacionUnaria, LRes):- predicadosuma(X, S),

Res=OperacionUnaria(S),

Page 10: VP - TEMA 2

operacion(Y,OperacionUnaria,Aux),

LRes=[Res|LAux].

GOAL

operacion([pares(3,2),

pares(2,1), pares(3,4)], cuadrado, ListaCuadrados),

operacion([pares(3,2),

pares(2,1), pares(3,4)], cubo, ListaCubos).

Vamos a explicar detalladamente cómo funciona el ejemplo propuesto para ilustrar el uso de la sección DOMAINS.

El predicado principal se denomina operacion y admite como argumentos una lista de predicados del dominio listapares, un predicado del dominio OperacionUnaria y una lista de enteros. Es interesante notar que en Visual Prolog podemos pasar como argumentos de nuestros predicados, no sólo datos y objetos compuestos sino también predicados y funciones que realizan diversas operaciones. De este modo, el predicado operacion recorre cada par de la lista, suma los números que componen dicho par y les aplica el predicado (operación unaria), pasado por parámetro, así el mismo predicado operacion sirve para calcular cuadrados, cubos, etc., sobre la suma de los pares de la lista original.

Como vemos unaria = determ INTEGER (INTEGER) - (i) es la definición de un dominio al que pertenecen todos los predicados que tengan esa forma. Cualquier predicado que realiza una operación unaria de este tipo encaja en ese dominio. Así pues en el ejemplo tenemos definidos los predicados cuadrado y cubo como del dominio unaria.

En la sección CLAUSES se define exactamente el cuerpo de ambos predicados que este caso actúan como funciones.

Para la meta especificada, el resultado de la ejecución es el siguiente:

ListaCuadrados=[25,9,49], ListaCubos=[125,27,343]

1 Solution

[ÍNDICE]

Sección de la base de datos

Page 11: VP - TEMA 2

Como sabemos un programa en Visual Prolog no es más que un conjunto de hechos y reglas. En ocasiones, podemos querer actualizar este conjunto de hechos mientras el programa se ejecuta. En tal caso, los hechos deben tratarse como un conjunto dinámico de datos que pertenecen a una base que puede ser actualizada y modificada convenientemente mientras dura la ejecución del programa.

Sólo los hechos que se declaran en la sección FACTS serán dinámicos y podrán ser actualizados en tiempo de ejecución.

La sección FACTS (también llamada DATABASE) tiene la siguiente forma:

[GLOBAL] {FACTS | DATABASE} [- nombre de la base de datos]

[nocopy][{nondeterm|determ|single}] hecho_1[([Lista_Args_hecho_1])]

...

[nocopy][{single|determ|nondeterm}] hecho_N[([Lista_Args_hecho_N])]

...

donde:

GLOBAL: determina si esta sección es o no de ámbito global a todo el proyecto.

nocopy: determina que los datos que se van a enlazar o unificar con una variable al hacer una llamada al hecho no se almacenarán temporalmente en la pila global del programa.

nodeterm: determina que pueden existir varias instancias para el hecho. determ: determina que sólo puede existir una instancia para el hecho en

el mismo momento. single: determina que sólo puede existir una y sólo una instancia para el

hecho.

[ÍNDICE]

Predicados definidos en Visual Prolog para manejar la base de datos

Page 12: VP - TEMA 2

assert(<Dominio del hecho> Hecho) - (i): Inserta un hecho tras otros encontrados pertenecientes al mismo predicado en la base de hechos. Si se trata de un hecho single lo sustituye. Este predicado nunca falla.

assert(<De correspondientes dominios del hecho> Hecho, NombreSeccionHechos) - (i,i): Inserta un hecho tras otros encontrados pertenecientes al mismo predicado en la base de hechos con el nombre especificado en NombreSeccionHechos. El hecho debe ser un término perteneciente al dominio de la sección de hechos dados en NombreSeccionHechos. Este predicado nunca falla.

asserta(<Dominio del hecho> Hecho) - (i): ): Inserta un hecho delante de otros encontrados pertenecientes al mismo predicado en la base de hechos. Este predicado nunca falla.

asserta(<De correspondientes dominios del hecho> Hecho, NombreSeccionHechos) - (i,i): Inserta un hecho delante de otros encontrados pertenecientes al mismo predicado en la base de hechos con el nombre especificado en NombreSeccionHechos. El hecho debe ser un término perteneciente al dominio de la sección de hechos dados en NombreSeccionHechos. Este predicado nunca falla.

assertz(<Dominio del hecho> Hecho) - (i): Como assert/1. Este predicado nunca falla.

assertz(<De correspondientes dominios del hecho> Hecho, NombreSeccionHechos) - (i,i): Como assert/2. Este predicado nunca falla.

consult(STRING OSNombreFichero) - (i): Sirve para leer hechos desde un fichero de hechos y almacenarlos en la base de hechos. Este fichero debe haber sido salvado con save.

consult(STRING OSNombreFichero, NombreSeccionHechos) - (i,i): Sirve para leer hechos desde un fichero de hechos y almacenarlos en la base de hechos de nombre NombreSeccionHechos. Este fichero debe haber sido salvado con save.

save(STRING OSNombreFichero) - (i): Sirve para salvar los hechos de la sección de hechos sin nombre en un fichero de texto.

save(STRING OSNombreFichero, NombreSeccionHechos) - (i,i): Sirve para salvar los hechos de una sección de hechos con nombre NombreSeccionHechos en un fichero de texto.

nondeterm retract(<Dominio del hecho> Hecho): Borra el primer hecho que encuentre que case con el hecho especificado en el argumento. Falla cuando no hay más hechos que borrar.

nondeterm retract(<De correspondientes dominios del hecho> Hecho, NombreSeccionHechos) - (i,i): Borra el primer hecho que encuentre que

Page 13: VP - TEMA 2

case con el hecho especificado en el argumento. Actúa sobre del dominio de hechos con nombre NombreSeccionHechos. Falla cuando no hay más hechos que borrar.

determ retractall(<Dominio del hecho> Hecho) - (i): Borra el todos los hechos encontrados que casen con el hecho especificado en el argumento. Nunca falla.

determ retractall(<De correspondientes dominios del hecho> Hecho, NombreSeccionHechos ) - (i,i): Borra todos los hechos encontrados que casen con el hecho especificado en el argumento. Actúa sobre del dominio de hechos con nombre NombreSeccionHechos.

Veamos ahora algunos ejemplos de uso de los predicados mostrados para manejar la base de hechos.

FACTS

padre(string, string)

PREDICATES

abuelo(string, string)

CLAUSES

padre(juan, pepe).

padre(juan, luis).

padre(pepe, manolo).

abuelo(X, Y):-padre(X, Z), padre(Z, Y).

GOAL

assert(padre(pepe, beatriz)),

assertz(padre(pepe, carlos)),

asserta(padre(pepe, maria)),

abuelo(juan, Y).

Tenemos 3 hechos que definen quién es padre de quién y un predicado para deducir el parentesco abuelo.

Tras la ejecución de la meta obtenemos el siguiente resultado:

Y=maria

Page 14: VP - TEMA 2

Y=manolo

Y=beatriz

Y=carlos

4 Solutions

Si examinamos paso a paso esta ejecución, lo que ha sucedido ha sido lo siguiente:

Tras el primer assert hemos insertado el hecho padre(pepe, beatriz) tras padre(pepe, manolo).

El predicado assertz funciona como assert, luego hemos insertado el hecho padre(pepe, carlos) tras el hecho padre(pepe, beatriz).

Por último asserta inserta al principio, luego el hecho padre(pepe, maria) ha quedado colocado el primero.

Veamos ahora un ejemplo del uso de retract y retractall.

FACTS

padre(string, string)

PREDICATES

abuelo(string, string)

CLAUSES

padre(juan, pepe).

padre(juan, luis).

padre(pepe, manolo).

padre(pepe, beatriz).

padre(pepe, carlos).

padre(pepe, maria).

abuelo(X, Y):-padre(X, Z), padre(Z, Y).

GOAL

retract(padre(pepe, _)), !,

padre(pepe, L).

Page 15: VP - TEMA 2

En este caso retract borra todas la primera ocurrencia de padre, siempre que su primer argumento sea pepe.

El corte que va después de retract sirve para que Visual Prolog no haga backtracking y borre todas las ocurrencias de padre(pepe, ...), ya que por defecto el compilador intenta ofrecer todas las posibles soluciones para una meta dada. Con el corte situado en ese lugar, sólo se hace bactracking sobre padre(pepe, L).

El resultado de la ejecución se muestra a continuación:

L=beatriz

L=carlos

L=maria

En el ejemplo siguiente, vemos que en lugar de usar retract estamos usando retractall para borrar todas las ocurrencias de padre(pepe, ...).

FACTS

padre(string, string)

PREDICATES

abuelo(string, string)

CLAUSES

padre(juan, pepe).

padre(juan, luis).

padre(pepe, manolo).

padre(pepe, beatriz).

padre(pepe, carlos).

padre(pepe, maria).

abuelo(X, Y):-padre(X, Z), padre(Z, Y).

GOAL

retractall(padre(pepe, _)),

padre(pepe, L).

El resultado de esta ejecución es:

No Solution

Page 16: VP - TEMA 2

[ÍNDICE]

Sección de definición de predicados

En esta sección se definen los predicados que pueden pertenecer a dominios definidos en la sección DOMAINS (punteros a predicados), o se definen los predicados con una forma determinada en caso de que no los hayamos agrupado en ningún dominio concreto (todas las reglas de la sección de cláusulas deben pertenecer a algún predicado). La sección de definición de predicados va precedida siempre de la palabra PREDICATES.

Por ejemplo podemos tener este caso:

DOMAINS

unaria = determ INTEGER (INTEGER) - (i)

PREDICATES

cubo: unaria

CLAUSES

cubo(E, Res):-Res=E*E*E.

O podemos tener este otro:

PREDICATES

cubo(INTEGER, INTEGER)

CLAUSES

cubo(E, Res):-Res=E*E*E.

En el primer caso cubo se define como una función y debe ser llamada como A=cubo(3) y en el segundo caso la llamada debe hacerse como cubo(3, A).

Ambas formas de definición son igualmente correctas y su uso dependerá de la aplicación en cuestión en la que estemos trabajando.

Punteros a predicados: su forma general es como sigue: Nombre: Dominio del predicado [namespec] donde:

o Nombre: es el nombre del puntero a predicado.o Dominio del predicado: es el dominio al que pertenece el predicado

y debe declararse en la sección DOMAINS.

Page 17: VP - TEMA 2

o [namespec]: puede ser usado para especificar el nombre de un código objeto público en una declaración global. Su principal uso se da cuando se linkan módulos escritos en otros lenguajes.

Predicados y funciones: su forma general es como sigue: [TipoPredicado] [DominioRetorno] NombrePredicado[(ListaArgumentos)] [- [flowpatterns]] [lenguaje] [namespec] donde:

o [TipoPredicado]: es de la forma {procedure | determ | nondeterm | failure | erroneous | multi}. Pueden declararse varios junto con los distintos flowpatterns seleccionados para el predicado.

o [DominioRetorno]: es el dominio de retorno si se está declarando una función.

o NombrePredicado: es el nombre del predicado.o [(ListaArgumentos)]: es la lista de argumentos del predicado.o [- [flowpatterns]]: los patrones de flujo indican como se usa cada

argumento de la lista de argumentos. Utilizaremos i para los argumentos de entrada y o para los argumentos de salida. Pueden declararse varios para un mismo predicado.

o [lenguaje]: es de la forma language { asm | c | pascal | prolog | stdcall | syscall }.

o [namespec]: es usado como en el caso de punteros a predicados.

[ÍNDICE]

Tipos de predicados

Como hemos visto, pueden existir 6 tipos de predicados en función de cómo y cuantas soluciones pueden aportar y si pueden o no fallar. Cada predicado puede tener varias cláusulas o reglas que lo implementa, por tanto, cláusulas y predicados deben ser coherentes.

Esta coherencia está relacionada con el concepto de determinismo en Visual Prolog.

La mayoría de los lenguajes son deterministas. Esto quiere decir que cualquier conjunto de datos de entrada conduce a un único conjunto de instrucciones para producir un conjunto de datos de salida. Sin embargo, en Visual Prolog se admite la inferencia no determinista basada en predicados no deterministas.

Visual Prolog ejerce un gran control sobre el determinismo de las reglas y predicados que aparecen en los programas, principalmente con el objeto de ahorrar espacio de almacenamiento en tiempo de ejecución. Cuando una cláusula

Page 18: VP - TEMA 2

o regla determinista que compone un predicado termina con éxito, el espacio de pila correspondiente puede ser dispensado rápidamente, liberando de este modo el espacio ocupado.

El sistema de control del determinismo de Visual Prolog es muy fuerte. De hecho, el compilador fuerza al programador, a través de este control, a declarar en sus predicados 2 aspectos de comportamiento importantes:

Si la llamada a un predicado puede fallar. Número de soluciones que un predicado puede producir.

De acuerdo a estos dos aspectos de determinismo, Visual Prolog soporta los siguientes tipos de predicados:

erroneous: nunca debe fallar y debe producir 0 soluciones. failure: puede fallar y debe producir 0 soluciones. nondeterm: puede fallar y puede producir múltiples soluciones. procedure: nunca debe fallar aunque pueden producirse errores en

tiempo de ejecución y debe producir una única solución. determ: puede fallar y debe producir una única solución, es decir, no

hacen backtracking. multi: nunca debe fallar y puede producir múltiples soluciones mediante

la técnica de backtracking.

El sistema de control de determinismo de Visual Prolog chequea la correspondencia de las definiciones de predicados con los modos de determinismo declarados para los predicados. Por defecto, el compilador verifica las cláusulas o reglas de predicados y da un warning si no puede garantizar que un predicado declarado como multi, procedure o erroneous nunca falla.

Todos los predicados se definen internamente con alguno de estos modos. Para los predicados definidos como determ, procedure, failure o erroneous el compilador dará un warning para cada cláusula o regla del programa que dé como resultado un predicado no determinista.

Hay dos tipos de cláusulas no deterministas:

Aquellas que no contienen un corte y hay una o más cláusulas que pueden casar (o unificarse) con los mismos argumentos de entrada para ese patrón de flujo de entrada.

Page 19: VP - TEMA 2

Aquellas que llaman a un predicado no determinista y dicha llamada no está seguida de un corte. Debido a esto, el no determinismo puede extenderse por muchos predicados a menos que se utilicen uno o más cortes.

Cuando se escriben predicados declarados como multi, procedure o erroneous se deben seguir un par de reglas:

Si alguna cláusula de predicado puede fallar se debe definir al final una cláusula que no falle seguro.

De acuerdo a los dominios declarados, para cualquier posible conjunto de elementos de entrada debe existir una cláusula que tenga una cabeza que case con dicho conjunto.

[ÍNDICE]

Ejemplos de cómo conseguir la coherencia entre reglas y predicados en el aspecto determinista

Como hemos dicho hay dos tipos de cláusulas no deterministas: uno de los tipos son aquellas que no contienen un corte y hay una o más cláusulas que pueden casar (o unificarse) con los mismos argumentos de entrada para ese patrón de flujo de entrada. Veamos un ejemplo de predicado definido como determinista con un conjunto de cláusulas que no son deterministas:

DOMAINS

list=integer*

PREDICATES

lista(list)

prueba

CLAUSES

lista([]).

lista([1]).

lista([1,2]):-write("Hola").

lista(L):-write(L).

prueba:-lista([1,2]),

nl,

Page 20: VP - TEMA 2

lista([1,2,3]).

GOAL

prueba.

Como vemos el conjunto de reglas para el predicado lista(list) son todas no deterministas, ya que no aparece un corte en ellas y hay varias que pueden casar con el mismo patrón de entrada. Por ejemplo, la lista vacía puede casar en lista([]) y en lista(L).

Para resolver la incoherencia podemos tomar dos soluciones:

Hacer lista(list) no determinista escribiendo delante la palabra reservada nondeterm.

Conseguir que cada cláusula o regla que compone el predicado sea determinista y para ello hay que hacer un uso correcto del corte. Esta segunda solución es más correcta y optimizada en este caso, ya que lo que se pretende en el ejemplo es que el predicado nos aporte una única solución, es decir no interesa que haga backtracking ni siquiera forzado.

Vamos a suponer que forzamos el fallo tal y como tenemos actualmente escrito el programa, para ello la única variación que hay que hacer es la siguiente:

prueba:-lista([1,2]),

nl,

lista([1,2,3]),

fail.

La ejecución del programa produce el siguiente resultado:

Hola

[1,2,3][1,2]

[1,2,3]

Veamos el árbol de ejecución que se ha generado en la Figura 1:

Page 21: VP - TEMA 2

Figura 1

Árbol de ejecución

Nos interesa que el programa aporte una única solución y no un conjunto de ellas, incluso si intentamos hacer backtracking de un modo forzado. El corte nos proporciona capacidad para podar el árbol de ejecución, por tanto, hemos de poner tantos cortes como se requieran para conseguir que el programa definido por el conjunto de reglas aporte una solución única en cualquier caso.

La modificación realizada, la ejecución del programa y el árbol de ejecución se presentan a continuación, ver Figura 2:

lista([]):-!.

lista([1]):-!.

lista([1,2]):-write("Hola"),!.

lista(L):-write(L).

Hola

[1,2,3]

Page 22: VP - TEMA 2

Figura 2

Árbol de ejecución

[ÍNDICE]

Sección de cláusulas

La sección de cláusulas contiene la especificación o implementación del conjunto de hechos y reglas que componen el programa. Dicha sección se encabeza con la palabra CLAUSES.

Una cláusula puede ser:

Un hecho: por ejemplo padre(juan, maria). Una regla de deducción: por ejemplo abuelo(X, Y):-padre(X, Z), padre(Z,

Y). Como se observa la regla de deducción se puede interpretar del modo siguiente, la parte izquierda es cierta si la parte derecha de la regla es cierta. Para ello deben ser ciertas todas las cláusulas separadas por conjunciones "," o, al menos, una de las que estén separadas por disyunciones ";". Si una parte de la regla falla, se intenta hacer backtracking con la inmediatamente anterior, y si esta es la parte izquierda de la cláusula con la cláusula siguiente en caso de que exista.

Page 23: VP - TEMA 2

Una secuencia de cláusulas que definen un predicado se denomina procedimiento.

[ÍNDICE]

Sección de meta u objetivo

En esta sección se coloca la meta u objetivo que deseamos que Prolog satisfaga. Es similar a cualquier otra regla, ya que la meta está compuesta de un conjunto de submetas que hay que demostrar, la principal diferencia reside en que detrás de la palabra GOAL no se pone ":-", es decir no existe parte izquierda de la regla, y ésta se ejecuta directamente cuando arranca el programa.

[ÍNDICE]

Ejemplo

[CÓDIGO FUENTE] [EJECUTABLE]

Vamos a ver un ejemplo completo de cómo realizar un programa en Visual Prolog haciendo uso de todas las secciones que hemos visto.

El programa debe gestionar una lista polimórfica de obras de arte. Las obras de arte pueden ser cuadros o esculturas. Los datos que se deben almacenar en un cuadro son el número de serie, el autor, el estilo y las dimensiones. Los datos que se deben almacenar en una escultura son el número de serie, el autor y el material.

El programa permitirá insertar en orden las obras y visualizará listados de cuadros y esculturas de forma separada.

Comencemos, pues a examinar el código construido.

DOMAINS

obra = cuadro (INTEGER, STRING, STRING, STRING);

escultura (INTEGER, STRING, STRING)

LIST = obra*

En la sección DOMAINS definimos el dominio obra, que agrupará dos tipos de objetos compuestos: cuadros o esculturas.

El dominio LIST define el tipo lista polimórfica de obras de arte.

Page 24: VP - TEMA 2

Gráficamente tenemos:

PREDICATES

obtenerNS(INTEGER, obra)

insorden(obra,LIST,LIST)

pedirdatoscuadro(STRING, STRING)

pedirdatosescultura(STRING)

insertar(LIST, LIST, CHAR, INTEGER, STRING)

inserta(LIST, LIST)

eligeobra(CHAR, INTEGER, STRING)

escribircuadro(obra)

escribirescultura(obra)

nondeterm recorrerlista(LIST, SYMBOL)

nondeterm ejecutaropcion(INTEGER, LIST, LIST)

programa(LIST)

menu(INTEGER)

En la sección PREDICATES hemos definido el nombre y el tipo de los argumentos de todos los predicados que se usarán durante el programa.

Page 25: VP - TEMA 2

En la sección CLAUSES tenemos el conjunto de reglas o cláusulas que implementan cada predicado.

Vamos a describir brevemente lo que hace cada regla.

CLAUSES

obtenerNS(Serie,Predicado):-Predicado=cuadro(Serie,_,_,_), !.

obtenerNS(Serie,Predicado):-Predicado=escultura(Serie,_,_).

La regla obtenerNS tiene dos parámetros: un INTEGER y una obra. El segundo parámetro es de entrada y el primer parámetro es de salida. Cuando unificamos la variable Predicado que tras la llamada a la regla estará instanciada, ésta almacenará o un cuadro o una escultura. Si almacena un cuadro la comparación Predicado=cuadro(Serie,_,_,_) será cierta y la variable Serie se instanciará con el valor del número de serie del cuadro. Si almacena una escultura, la primera regla fallará y pasaremos a comparar el Predicado con el objeto escultura(Serie,_,_). La unificación será posible ya que si no es un cuadro es una escultura y en este caso estaremos obteniendo el número de serie de una escultura.

insorden(E,[],[E]):-!.

insorden(E,[X|Y],[X|Cola]):-obtenerNS(Serie1,E),

obtenerNS(Serie2,X),

Serie1>Serie2,

insorden(E,Y,Cola), !.

insorden(E,LOr,[E|LOr]).

El predicado insorden admite tres parámetros: un elemento de entrada de tipo obra y otro de tipo LIST y por último un elemento de salida también de tipo LIST.

El predicado está implementado mediante tres reglas:

La primera regla satisface los objetivos en los que se pretenda insertar un elemento en una lista vacía.

La segunda regla satisface los objetivos en los que se pretenda insertar un elemento que es mayor que el que está a la cabeza de la lista en ese instante. En este caso es necesario seguir recorriendo la lista mediante una llamada recursiva para insertar el elemento en la posición correcta.

Page 26: VP - TEMA 2

La tercera regla satisface los objetivos en los que se pretenda insertar un elemento inmediatamente porque ya se ha encontrado el lugar adecuado para el mismo.

pedirdatoscuadro(Estilo, Dimensiones):-nl, write("Estilo: "),

readln(Estilo),

write("Dimensiones: "),

readln(Dimensiones).

El predicado pedirdatoscuadro acepta dos parámetros de salida: el estilo y las dimensiones del cuadro. La regla que lo implementa simplemente rellena las variables con los contenidos capturados a través del teclado.

pedirdatosescultura(Material):-nl, write("Material: "),

readln(Material).

El predicado pedirdatosescultura acepta un parámetro de salida: material de la escultura. La regla que lo implementa simplemente rellena la variable con el contenido capturado a través del teclado.

insertar(L,NuevaL,Seleccion, NS, Autor):-Seleccion='c',

pedirdatoscuadro(Estilo, Dimensiones),

Registro=cuadro(NS, Autor, Estilo, Dimensiones),

insorden(Registro,L,NuevaL), !.

insertar(L,NuevaL,Seleccion, NS, Autor):-Seleccion='e',

pedirdatosescultura(Material),

Registro=escultura(NS, Autor, Material),

insorden(Registro,L,NuevaL).

El predicado insertar acepta cinco parámetros, todos de entrada salvo NuevaL que es de salida. Las reglas que implementan este predicado funcionan de modo similar. Si el parámetro Seleccion contiene el carácter "c" en el objetivo propuesto, ejecutaremos la primera regla, de modo que estaremos insertando un cuadro. Si el parámetro Seleccion contiene el carácter "e", ejecutaremos la segunda regla, de modo que estaremos insertando una escultura en la lista NuevaL.

eligeobra(Seleccion, NS, Autor):-write("Escribe el Número de Serie: "),

Page 27: VP - TEMA 2

readint(NS),

write("Escribe el Nombre del Autor: "),

readln(Autor),

write("Selecciona [c]=Cuadro o [e]=Escultura -> "),

readchar(Seleccion).

El predicado eligeobra acepta tres parámetros de salida. En la regla que lo implementa las variables se rellenan con los contenidos obtenidos a través del teclado.

inserta(L, NuevaL):-eligeobra(Seleccion, NS, Autor),

insertar(L,NuevaL,Seleccion, NS, Autor).

El predicado inserta acepta dos parámetros de tipo LIST, el primero de entrada y el segundo de salida. La regla que lo implementa se satisface mediante la resolución de la regla eligeobra y posteriormente con una llamada a insertar con los resultados obtenidos de la resolución de la regla precedente.

escribircuadro(cuadro(NS,Autor,Estilo,Dimensiones)):-

write("Obra de Arte => CUADRO"), nl,

write("Número de Serie: "), Aux1=NS, write(Aux1), nl,

write("Autor: "), Aux2=Autor, write(Aux2), nl,

write("Estilo: "), Aux3=Estilo, write(Aux3), nl,

write("Dimensiones: "), Aux4=Dimensiones, write(Aux4), nl,

write("---------------------------------"), nl,

write("Pulsa para continuar..."),

readint(_).

El predicado escribecuadro acepta por parámetro un elemento de tipo obra, luego la regla que lo implementa acepta como parámetro un objeto compuesto de tipo cuadro que está englobado dentro del dominio obra. Esta regla simplemente actualiza los parámetros del objeto compuesto con los contenidos obtenidos desde teclado.

escribirescultura(escultura(NS, Autor, Material)):-

write("Obra de Arte => ESCULTURA"), nl,

write("Número de Serie: "), Aux1=NS, write(Aux1), nl,

Page 28: VP - TEMA 2

write("Autor: "), Aux2=Autor, write(Aux2), nl,

write("Material: "), Aux3=Material, write(Aux3), nl,

write("---------------------------------"), nl,

write("Pulsa para continuar..."),

readint(_).

El predicado escribeescultura acepta por parámetro un elemento de tipo obra, luego la regla que lo implementa acepta como parámetro un objeto compuesto de tipo escultura que está englobado dentro del dominio obra. Esta regla simplemente actualiza los parámetros del objeto compuesto con los contenidos obtenidos desde teclado, como en el caso anterior.

recorrerlista([], _):-!.

recorrerlista([X|Y],Que):- Que=cu,

escribircuadro(X),

recorrerlista(Y, Que),!.

recorrerlista([X|Y],Que):- Que=es,

escribirescultura(X),

recorrerlista(Y, Que),!.

recorrerlista([_|Y],Que):-recorrerlista(Y, Que).

El predicado recorrerlista acepta dos parámetros de entrada. Está implementado mediante cuatro reglas:

La primera satisface el objetivo en el que la lista a recorrer esté vacía. La segunda y tercera regla satisfacen objetivos en los que el parámetro

Que decide que tipo de obra de arte hay que visualizar, bien un cuadro o bien una escultura.

La cuarta regla satisface objetivos en los que no hemos de visualizar el elemento cabeza de la lista en ese instante porque no coincide con el tipo de elemento que deseamos mostrar.

ejecutaropcion(0,L,L):-recorrerlista(L,cu),!.

ejecutaropcion(1,L,L):-recorrerlista(L,es),!.

ejecutaropcion(2,L,NuevaL):-inserta(L,NuevaL).

Page 29: VP - TEMA 2

El predicado ejecutaropcion admite 3 parámetros, de los cuales los dos primeros son de entrada y el último de salida. Tres posibles reglas implementan el predicado:

La primera se satisface cuando el usuario selecciona la opción 0 a través del menú. Eso significa que hay que visualizar todos los cuadros de la lista.

La segunda se satisface cuando el usuario selecciona la opción 1 a través del menú. En este caso hay que mostrar todas las esculturas.

La tercera regla es satisfecha cuando el usuario selecciona la opción 2, y permite insertar un nuevo elemento en la lista mediante una llamada al predicado inserta.

menu(Opcion):-write("------------ MENU ------------"),nl,

write("- 0.- Listado de Cuadros -"),nl,

write("- 1.- Listado de Esculturas -"),nl,

write("- 2.- Insertar elementos -"),nl,

write("- 3.- Salir -"),nl,

write("------------------------------"),nl,

write("Opción: "),

readint(Opcion),

write("---------------------------------"), nl.

El predicado menu acepta un parámetro de salida. La regla que lo implementa muestra al usuario el conjunto de posibles opciones y almacena en la variable Opcion el contenido seleccionado a través de teclado.

programa(L):-menu(Opcion),

Opcion<>3,

ejecutaropcion(Opcion,L,NuevaL), !,

programa(NuevaL),!.

programa(_):-write("Hasta pronto..."), nl.

El predicado programa admite como parámetro de entrada una lista de obras de arte. Está implementado mediante dos reglas:

Page 30: VP - TEMA 2

La primera que presenta el menú de opciones y comprueba si se ha seleccionado la opción 3. En ese caso falla y pasa a satisfacer la segunda regla que implementa este predicado. Si la opción seleccionada no es la 3, se llama al predicado ejecutaropcion para tratar la lista, y por último se hace una llamada recursiva a programa para que esta vez actúe sobre la nueva lista obtenida.

La segunda regla implementa lo que debe satisfacerse en caso de que el usuario haya elegido la opción de salir de la aplicación.

GOAL

programa([]).

La meta definida es una llamada a programa pasándole por parámetro una lista vacía, esta llamada se unificará con la primera regla del predicado programa y dispondremos ya de la variable local L iniciada para comenzar a trabajar con nuestra lista de obras de arte.

[ÍNDICE]

El entorno de desarrollo de Visual PrologEl entorno de desarrollo de Visual Prolog nos proporciona la capacidad de generar aplicaciones lógicas orientadas a eventos para Windows 95 y 98 de forma sencilla. Disponemos de capacidades de creación de programas de forma visual y esto implica que el código que implementa la interfaz de usuario sea generado en su mayor parte por el propio Experto del entorno de desarrollo.

De lo que se tiene que encargar el programador, por tanto, es de especificar las tareas que se deben llevar a cabo como respuesta a los sucesos o eventos que se producen en el sistema.

Antes de profundizar en el estudio de este entorno, demos un repaso breve a los conceptos sobre los que se apoya la programación orientada a eventos.

[ÍNDICE]

Aplicaciones orientadas a eventos

En una aplicación DOS, el programador fija la secuencia de operaciones en tiempo de diseño. No existe apenas interacción entre la aplicación diseñada y otras aplicaciones y el sistema operativo no produce interferencias.

Page 31: VP - TEMA 2

Las aplicaciones DOS suelen comunicarse directamente con el usuario y se basan en una filosofía de implementación estática.

Los sistemas Windows, en cambio, soportan una filosofía dinámica. En estos casos, el sistema operativo provee a las aplicaciones de una capa de servicios para su comunicación con el usuario, de este modo el sistema operativo se convierte en un objeto activo que el programador debe conocer ya que participa en la ejecución y secuenciación de las aplicaciones dentro de un ámbito multitarea. Las aplicaciones deben contemplar esta característica y la metodología de construcción de las mismas se denomina diseño orientado a eventos.

Cuando el usuario realiza una operación para interactuar con el sistema (pulsar una tecla, mover el ratón,...), se produce un suceso o evento. Dicho evento es generado desde el interfaz hardware y el sistema operativo lo traduce a un mensaje con formato estándar y envía una notificación de evento al propietario correcto que generalmente es la ventana activa (la que tiene el foco).

Los eventos pueden llegar a la aplicación de forma asíncrona, por ello es necesario escribirla cuidadosamente, notando en cada momento que nos movemos en un entorno multitarea donde el resto de aplicaciones y el sistema operativo son objetos plenamente activos, para conseguir que se mantenga la integridad tanto de procesos como de datos.

Para cada evento debe existir un manejador de evento, que contiene el código que se ha de ejecutar tras producirse el suceso al que se encuentra vinculado.

Cuando el programador escribe el programa asocia un manejador a cada evento que desea que se trate. Los manejadores, normalmente, son rutinas con una cabecera preestablecida y un cuerpo vacío que debe ser rellenado con las instrucciones apropiadas.

Si el programador no define un manejador propio para un evento determinado, dicho evento es tratado por defecto por el sistema.

[ÍNDICE]

Aplicaciones orientadas a eventos en Visual Prolog

La interfaz de un programa en Visual Prolog va a ser simplemente un conjunto de ventanas de aspectos y tipos distintos organizadas mediante una jerarquía.

La ventana principal se denomina ventana de tareas o Task Window.

Page 32: VP - TEMA 2

Cada ventana tiene un número que la identifica dentro del sistema, ese número se denomina manejador (handler) de la ventana.

Dado que las ventanas se encuentran organizadas en un sistema jerárquico podemos hablar de que cada ventana tiene un padre.

Cada ventana lleva asociado un predicado que maneja todos los eventos para la misma. Dicho predicado pertenece al dominio EHANDLER y tiene dos argumentos importantes:

El manejador de la ventana: handler. Un predicado que describe el evento producido, de ahora en adelante

evento.

Además los predicados del dominio EHANDLER pueden devolver valores a través de un parámetro de retorno del dominio LONG que se usan en muy pocos casos. Normalmente, se devuelve 0.

El entorno de desarrollo de Visual Prolog proporciona un diálogo llamado Experto a partir del cual se generará parte del código que tiene que ver con la implementación de la interfaz de la aplicación.

Los predicados manejadores de evento para cada ventana deben ser generalmente declarados y creados a través del Experto. El programador debe escribir personalmente el código a incluir en el cuerpo de los manejadores de evento ya declarados por el Experto para los eventos que desee controlar de forma explícita. El resto de eventos serán manejados por defecto por el sistema.

[ÍNDICE]

Tipos de ventanas en Visual Prolog

Como hemos comentado anteriormente, la interfaz de una aplicación no es más que un conjunto de ventanas organizadas de modo jerárquico.

Sin embargo, no todas las ventanas tienen la misma finalidad: un botón no es lo mismo que un diálogo.

Veamos los diferentes tipos de ventanas que existen:

Windows: Existen distintos tipos de ventanas dependiendo del lugar que ocupen en la jerarquía, Tabla 2.

Page 33: VP - TEMA 2

Tipo Descripción

Task Window

Es una abstracción de la aplicación en sí misma. Es la ventana principal del programa y la inicialización y finalización de éste están siempre asociados a dicha ventana. Bajo el modo MDI (Multiple Document Interface) la ventana principal actúa como contenedor de las ventanas hijas.

Screen Window Es una abstracción que representa la pantalla entera. Esta ventana siempre es el padre de la Task Window.

Top-Level Window

Se trata de un documento normal de Windows cuyo padre puede ser la TaskWindow o la Screen Window. Pueden almacenar ventanas hijas y controles y, a menudo, contienen menús y pueden ser maximizadas y minimizadas.

Child WindowsEstán restringidas a permanecer dentro del su ventana padre. Cuando la ventana padre es cerrada, destruida, minimizada, etc., también lo son las ventanas hijas.

Print Ventana interna usada durante el proceso de impresión

Picture Ventana interna usada durante el proceso de creación de un gráfico, imagen o dibujo.

Metafile Ventana interna usada durante la creación de un metafile.

Tabla 2

Dialogs: Los diálogos son un tipo especial de ventanas, cuya funcionalidad está reducida con respecto a las ventanas normales. Los diálogos normalmente se rellenan con controles, que puede interactuar con el usuario para visualizar salidas, aceptar entradas, ofrecer métodos de selección de opciones o permitir la edición de texto. Los diálogos pueden ser de dos tipos, Tabla 3:

Tipo Descripción

Diálogo modal Hasta que el diálogo no es cerrado no se puede acceder a las ventanas de la aplicación que se encuentran debajo de dicho diálogo.

Diálogo no modal Es posible acceder a lo que está debajo del diálogo antes de haberlo cerrado.

Tabla 3

Controls: Son pequeñas ventanas con una apariencia especial y una funcionalidad fija proporcionada por el sistema operativo. Se usan dentro de conjuntos fijos de controles dentro de otras ventanas, principalmente como componentes de diálogos. Los controles proveen al usuario la capacidad de escribir texto, llamar a comandos o elegir opciones. Los eventos que se generan por la manipulación de los controles causan notificaciones de mensajes que se envían al manejador de eventos de la ventana padre. Existen varios tipos de controles, Tabla 4:

Tipo DescripciónPushButton Botón típico que puede apretarse y soltarse.

Page 34: VP - TEMA 2

RadioButton Botón de aspecto redondo que suele estar agrupado con otros de la misma clase y sólo uno de ellos permanece activo simultáneamente.

CheckBox Botón de aspecto cuadrado que puede estar o no marcado.

HScroll Barra de scroll horizontal.

VScroll Barra de scroll vertical.

Edit Cuadro o ventana de edición de texto.

Text Etiqueta de texto estático.

LBox Lista de opciones o cadenas que pueden ser seleccionadas.

LBoxButton Lista desplegable de opciones.

LBoxEdit Lista desplegable de texto que puede editarse.

Icon Icono.

Custom Control personalizado.

Tabla 4

Los identificadores de los tipos de ventanas pertenecen al dominio WINTYPE y tienen w_ delante del nombre especificado para las ventanas, wc_ para los controles y wd_ para los diálogos.

Cuando se crea cualquier tipo de ventana o diálogo existen varios flags que determinan la apariencia del diálogo y que permiten conocer el estado del objeto sobre el que se está actuando.

[ÍNDICE]

El diálogo Experto del entorno de desarrollo VPI

Cuando desarrollamos cualquier aplicación en Visual Prolog el proceso de generación de los ficheros que van a componer el programa se realiza siempre de un modo similar.

Para ello accedemos a la opción New Project del menú principal Project, ver Figura 3 .

El diálogo que aparece nos permite configurar el método de desarrollo de nuestra aplicación de una forma muy sencilla. Los pasos a seguir son los siguientes:

1. Establecer un nombre para la aplicación en la caja de texto Project Name.2. Seleccionar un directorio base para la aplicación mediante la utilización

de la caja de texto Base Directory.

Page 35: VP - TEMA 2

3. Accionar la pestaña Target. A través de ella se configura el tipo de plataforma donde se ejecutará el programa, el tipo de estrategia de interfaz de usuario que se utilizará, si lo que se pretende generar es una aplicación exe o una dll y por último, seleccionar el lenguaje de programación que se usará para generar el programa principal. De no seleccionar VPI como estrategia de generación de la interfaz de usuario las opciones de la pestaña VPI Options aparecerán desactivadas, en caso contrario se permitirá seleccionar el conjunto de paquetes que se incluirán en la aplicación para montar la interfaz.

4. Accionar el resto de pestañas para configurar otros objetos más avanzados como el tipo de generación de código, la inclusión de objetos para manejar bases de datos, la introducción de los datos personales del programador y la colocación de elementos de ayuda en la aplicación.

Page 36: VP - TEMA 2

Figura 3

Aspecto del Experto de creación de una aplicación

Si se genera una aplicación sin entorno VPI, el método de programación no será ni visual ni orientado a eventos y deberemos comportarnos como en la escritura de cualquier aplicación realizada para DOS.

Vamos a considerar, sin embargo, aplicaciones con entorno VPI por tanto, lo primero que debemos saber es que VPI integra un conjunto de librerías en

Page 37: VP - TEMA 2

lenguaje Prolog que podemos usar y llamar para utilizar los servicios de interfaz proporcionados por el sistema Windows. Nos interesa conocer el uso de VPI, no cómo está construido por dentro.

La arquitectura en la que nos vamos a mover se muestra a continuación:

Conocemos, hasta ahora, el concepto de programación orientada a eventos, la estructuras de Prolog, los tipos de ventanas que ofrece VPI y la arquitectura de comunicación entre aplicación-VPI-Windows. También conocemos como crear una aplicación en vacío. Veamos, a continuación, cómo se utiliza el Diálogo Experto para colocar, configurar y crear las cabeceras de los manejadores de evento de los elementos implicados en la interfaz y funcionamiento de la aplicación con un ejemplo sencillo.

[ÍNDICE]

El primer programa VPI

[FUENTES EN FORMATO ZIP] [EJECUTABLE EXE]

Nuestro primer programa VPI va a ser el sistema experto de cálculo de velocidades en función de las características de la vía y el vehículo que hemos estudiado en teoría. Vamos a especificar, a continuación, todos los pasos de diseño e implementación a realizar.

Nuestro diseño descendente se muestra en el siguiente gráfico:

Page 38: VP - TEMA 2

El diseño de la interfaz de usuario puede ser como se muestra a continuación.

La pantalla principal mostrará dos apartados:

para describir cómo es la vía para seleccionar el tipo de vehículo

Cuando se pulsa el botón Calcular aparece un diálogo que muestra la velocidad para esa configuración de vía y vehículo.

Page 39: VP - TEMA 2

Ya tenemos el diseño descendente y el diseño de pantallas, pasemos ahora a la implementación del programa.

Lo primero es crear con el Experto una aplicación en vacío con todos los componentes VPI necesarios, en este caso: Task Window con menú y la posibilidad de generar diálogos.

A continuación aparece el siguiente diálogo:

En la parte izquierda tenemos activos los botones que nos permiten crear nuevos componentes para la aplicación. En la parte derecha aparecen los botones que permiten editar y borrar componentes añadidos, así como editar sus atributos, y acceder a la ventana de código experto que nos permitirá crear fácilmente las cabeceras de nuestros manejadores de evento.

Si ejecutamos la aplicación aparece una ventana vacía con tres elementos de menú: File, Edit y Help. Como ninguno de esos elementos nos interesa los vamos

Page 40: VP - TEMA 2

a eliminar y pondremos en su lugar una pestaña con el nombre Menú que va a contener la opción Salir.

Para ello pulsamos sobre el botón Menu del Experto y seleccionamos Task Menu con doble click. A través del diálogo que sigue, eliminamos las opciones no interesantes.

Antes de eliminar las opciones del menú nos tenemos que asegurar de eliminar también todo el código asociado a cada evento producido por la selección de cualquiera de las opciones. Para ello es necesario observar la ventana de Código Experto y buscar en ella los eventos asociados al menú para eliminar las cláusulas manejadoras introducidas automáticamente por el sistema.

Page 41: VP - TEMA 2

Si seleccionamos en el Diálogo Experto la Task Window y hacemos doble click obtenemos un panel y una serie de componentes que podemos pegar sobre la ventana.

Page 42: VP - TEMA 2

Controls nos proporciona un conjunto de controles que podemos pegar en la ventana. Layout provee herramientas para colocar los elementos dentro de la ventana.

La ventana principal de nuestra aplicación tiene el siguiente aspecto tras haber pegado los controles adecuados, en el esquema mostrado se especifica los nombres de los controles dinámicos, es decir, aquellos que van a permitir interactuar al usuario con el sistema. El resto de controles son etiquetas estáticas de información.

Vemos, ahora, el diálogo ya diseñado que debe aparecer tras la pulsación del botón Calcular. Posee tres elementos: dos etiquetas, una de información y otra para volcar el valor de la velocidad en ella y un botón de aceptación. Como el diálogo es modal, hasta que no lo cerremos no podremos actuar sobre la ventana principal.

Page 43: VP - TEMA 2

Ya tenemos el diseño de los formularios preparados. Con sólo pegar los elementos en la ventana principal se ha ido incluyendo el código necesario para que los controles funcionen adecuadamente. Necesitamos además incluir el código del diálogo en el fichero del programa. Para ello accedemos a su código experto y especificamos que deseamos que dicho código se incluya en el fichero adecuado que será, en este caso, el archivo autowin.pro.

Como se observa, el código debe actualizarse en autowin.pro a partir de la primera vez que se haya pulsado el botón Update Code.

Cada vez que haya que generar un manejador de evento o editar uno ya existente podemos utilizar este tipo de diálogo para cualquier ventana que tengamos definida.

Vamos a observar ahora cual es la jerarquía de ventanas del programa y cuáles son las dependencias entre elementos.

Page 44: VP - TEMA 2

El manejador de ventana de la Task Window recoge todos los eventos y se encarga de probar los subobjetivos correspondientes en función del evento pasado por parámetro, que puede provenir del botónCalcular o de la pulsación del menú.

El manejador del diálogo recoge los eventos de la ventana que lo enmarca y del botón de aceptación.

Nosotros debemos incluir código al manejar el evento de creación de la Task Window, en el evento de pulsación del botón Calcular y en el evento de creación del Diálogo. El resto de eventos son manejados automáticamente por el sistema.

[ÍNDICE]

Manejo del evento de creación de la Task Window.

El código que hemos de añadir es el que está resaltado en cursiva:

%BEGIN Task Window, e_Create

task_win_eh(_Win,e_Create(_),0):-!,

%BEGIN Task Window, InitControls, 14:04:04-4.10.2000, Code automatically updated!

...

...

...

%BEGIN Inicialización de la LIST BOX

W=win_GetCtlHandle(_Win, id_senales),

L=["autopista","autovia","viarapida","de_via_urbana","de_travesia","nohay"],

Page 45: VP - TEMA 2

lbox_Add(W, L),

%END Inicialización de la LIST BOX

!.

%END Task Window, e_Create

Como la ListBox es hija de la Task Window y el predicado lbox_Add añade una lista de cadenas a la ListBox siempre que conozcamos el manejador del control, necesitamos obtener el manejador del control a través del manejador del padre _Win que viene pasado por parámetro. Para ello utilizamos el predicado winGetHandle que devuelve el manejador del control a partir del manejador de ventana del padre y del identificador del control que en este caso es id_senales.

Rellenamos una lista con los items deseados y se la pasamos a lbox_Add.

[ÍNDICE]

Manejo del evento de pulsación del botón Calcular.

%BEGIN Task Window, idc_calcular _CtlInfo

%Manejador de código para la pulsación del botón

task_win_eh(_Win,e_Control(idc_calcular,_CtrlType,_CtrlWin,_CtlInfo),0):-

W_lbox=win_GetCtlHandle(_Win, id_senales),

lbox_GetSel (W_lbox, LS, _), %Obtención del tipo de carretera

LS=[Senal|_],

W_CBox1=win_GetCtlHandle(_Win, idc_más_de_1_carril),

Mas_de_1_carril=win_IsChecked (W_CBox1), %Obtención del número de carriles

W_CBox2=win_GetCtlHandle(_Win, idc_carril_de_adelantamiento),

Carril_de_adelantamiento=win_IsChecked (W_CBox2), %Obtención de inf. sobre carril de adelantamiento

W_Edicion=win_GetCtlHandle(_Win,idc_editaarcen),

ArcenStr=win_GetText (W_Edicion),

str_int(ArcenStr,Arcen), %Obtención del tamaño del arcén

tipovehiculo(_Win, Veh), %Obtención del tipo de vehículo

obtenernumerocarriles(Mas_de_1_carril, Carril_de_adelantamiento, Carriles),

Page 46: VP - TEMA 2

velocidad(Veh, Arcen, Carriles, Senal, VelocidadMaxima),

assert(la_velocidad(VelocidadMaxima), misdatos),

dlg_calculo_Create(_Win),

!.

task_win_eh(_Win,e_Control(idc_calcular,_CtrlType,_CtrlWin,_CtlInfo),0):-dlg_Error("Selecciona adecuadamente los parámetros"),!.

%END Task Window, idc_calcular _CtlInfo

La cabecera del evento ha sido generada por el sistema:

task_win_eh(_Win,e_Control(idc_calcular,_CtrlType,_CtrlWin,_CtlInfo),0)

y el objeto e_Control(idc_calcular,_CtrlType,_CtrlWin,_CtlInfo) nos informa de que ha sido el botón el que ha producido el evento.

Este evento se va a tratar de dos maneras, si la primera falla es porque el programa no puede hacer los cálculos correctamente y alguna cláusula falla debido a una mala introducción de los datos por parte del usuario, por tanto la máquina Prolog busca la siguiente cláusula para satisfacer que en este caso es la que visualiza un pequeño diálogo de error.

Cuando el sistema genera automáticamente la cabecera de un manejador de evento, la forma del cuerpo suele ser generalmente vacía:

cabecera(argumentos):-!, #punto para insertar código#

!.

Se puede observar que hay dos cortes uno al principio y otro al final. Esto debe dejarse así si el manejo de eventos se realiza con una sola cláusula, sin embargo si se realiza con varias como es el caso deCalcular hay que eliminar el primer corte para permitir la búsqueda de nuevas soluciones a través del proceso de backtracking.

Los subobjetivos planteados en la primera cláusula que compone el manejador, recogen los datos de los controles de la ventana principal, que el usuario debe haber rellenado. Una vez que los tiene, ejecuta el predicado velocidad que ya vimos en el caso de la aplicación no VPI en teoría. El predicado devuelve un integer en la variable local VelocidadMaxima.

Page 47: VP - TEMA 2

Como en Prolog no existen las variables globales no podemos pasar el contenido de dicha variable a la etiqueta que debe visualizarlo en el diálogo de un modo sencillo. Podemos intentar conseguir el manejador del diálogo a través de su padre, y a continuación intentar obtener el manejador del control a través del obtenido del diálogo, aunque esta opción es un poco complicada.

Otra posibilidad es hacer uso de hechos que pueden ser generados dinámicamente. Podemos generar un hecho del tipo la_velocidad(INTEGER) que sea single. Así pues en el manejador de botón almacenamos un hecho con la velocidad calculada mediante assert, y en el evento de creación del diálogo podemos capturar esta velocidad extrayendo el argumento del hecho almacenado.

[ÍNDICE]

Manejo del evento de creación del Diálogo de Cálculo.

%BEGIN calculo, e_Create

dlg_calculo_eh(_Win,e_Create(_CreationData),0):-!,

la_velocidad(V),

W_Texto=win_GetCtlHandle(_Win, idct_calculo_1),

str_int (V_text, V),

win_SetText(W_Texto, V_Text),

!.

%END calculo, e_Create

El resto de código está perfectamente comentado e implementado en el fichero fuente que podéis encontrar en la página de la asignatura.

El aspecto de la aplicación diseñada se muestra a continuación:

Page 48: VP - TEMA 2

TEMA 2Aritmética, datos y estructuras en Visual Prolog

 1 Sintaxis Básica de VISUAL PROLOG

1.1 Aritmética de VISUAL PROLOG

2 Datos simples y Estructuras de Datos: DOMINIOS

[2.1 OBJETOS COMPUESTOS] [2.2 ÁRBOLES] [2.3 LISTAS] [2.4 MATRICES]

3 Bibliografía

 

Page 49: VP - TEMA 2

Sintaxis Básica de VISUAL PROLOGHemos visto ya un buen número de convenciones que debemos utilizar para escribir programas en Prolog. La última parte de este tema la dedicaremos a conocer detalladamente las reglas sintácticas que hemos de seguir para que nuestras bases de conocimientos sean reconocidas por Visual Prolog.

Como ya hemos estudiado, un programa Prolog no es más que la especificación de una base de conocimientos lógica con las características siguientes:

Consta de una secuencia de oraciones, todas ellas verdaderas, que expresan un conjunto de realidades sobre los objetos del mundo representado, así como sobre sus relaciones. Todas las variables tienen cuantificación universal implícita y cuando las variables aparecen en oraciones distintas se consideran también distintas.

Se aceptan únicamente oraciones en forma de Cláusula de Horn, es decir, las oraciones son atómicas, o bien una implicación con un antecedente no negado y un consecuente generalmente"expresiones" separados por comas o por puntos y comas. Las comas significan conjunción y los puntos y comas significan disyunción.

En vez de utilizar antecedentes negados en sus implicaciones, en Prolog se emplea un operador de negación basado en el fallo: una meta no P se considera demostrada si el sistema no logra demostrar P.

[ÍNDICE]

Aritmética de VISUAL PROLOG

Las expresiones aritméticas en Visual Prolog se componen de operandos (números y variables), operadores (+, -, *, /, div, y mod) y paréntesis: A = 1 + 6 / (11 + 3) * Z. Ver Tabla 1.

Los números "0x" o "0o" significan hexadecimal y octal respectivamente: 0xFFF = 4095; 86 = 0o112 + 12.

El valor de una expresión se puede calcular si todas las variables están unificadas en el momento de la evaluación. El cálculo ocurre entonces en un orden determinado por la prioridad de los operadores aritméticos. Los operadores de mayor prioridad son evaluados primero. Ver Tabla 2.

[ÍNDICE]

Operaciones

Page 50: VP - TEMA 2

Operando 1 Operador Operando 2 Resultado

entero +, -, * entero entero

real +, -, * entero real

entero +, -, * real real

real +, -, * real real

entero ó real / entero ó real real

entero div entero entero

entero mod entero entero

Tabla 1

[ÍNDICE]

Orden de evaluación

Si la expresión contiene subexpresiones entre paréntesis, las subexpresiones se evalúan primero.

Si la expresión contiene multiplicación o división, estas operaciones son realizadas trabajando de izquierda a derecha a través de la expresión.

Las operaciones de suma y resta son llevadas a cabo de izquierda a derecha también.

En el orden de evaluación se tiene en cuenta, lógicamente, la precedencia de los operadores.

Operador Prioridad

+ - 1

* / mod div 2

- + (unario 3

Tabla 2

[ÍNDICE]

Funciones y predicados

Page 51: VP - TEMA 2

Visual Prolog posee una gran cantidad de funciones y predicados matemáticos para realizar las más variadas operaciones. La lista completa se ofrece en la Tabla 3.

Nombre Descripción

X mod Y Resto de X dividido entre Y.

X div Y Cociente de X dividido entre Y.

abs(X) Valor absoluto de X.

cos(X) Coseno de X.

sin(X) Seno de X.

tan(X) Tangente de X.

arctan(X) Arcotangente de X.

exp(X) e elevado al valor almacenado en X. (Exponencial).

ln(X) Logaritmo neperiano de X.

log(X) Logaritmo en base 10 de X.

sqrt(X) Raíz cuadrada de X.

random(X) Almacena en X un número aleatorio real entre 0 y 1.

random(X, Y) Almacena en Y un número aleatorio en el intervalo 0 <= Y < X.

round(X) Valor redondeado de X. El resultado es un número real.

trunc(X) Valor truncado de X. El resultado es un número real.

val(domain,X) Conversión explícita entre dominios numéricos.

Tabla 3

[ÍNDICE]

Comparaciones

En Visual Prolog podemos comparar expresiones aritméticas, caracteres, cadenas de caracteres y símbolos.

Las comparaciones de este tipo se realizan a través de operadores relacionales. Ver Tabla 4.

Page 52: VP - TEMA 2

Símbolo Relación

< menor que

<= menor o igual que

= igual que

> mayor que

>= mayor o igual que

<> o >< distinto

Tabla 4

[ÍNDICE]

Comparación de caracteres, cadenas de caracteres y símbolos

Además de las expresiones numéricas, podemos comparar caracteres, cadenas y símbolos: 'a' < 'b'; "antony" > "antonia" y P1 = peter, P2 = sally, P1 > P2.

Caracteres: Visual Prolog convierta la comparación 'a' < 'b' a su expresión aritmética correspondiente 97 < 98, usando el código ASCII correspondiente a cada carácter.

Cadenas de caracteres: Cuando se comparan dos cadenas o símbolos, la comparación se realiza carácter a carácter en sus correspondientes posiciones. El resultado es el mismo que se consigue comparando el carácter inicial a menos que los dos sean iguales en cuyo caso se pasa al siguiente. Cuando se encuentran dos caracteres iguales, el proceso de comparación termina y se produce el resultado.

Símbolos: No pueden ser comparados directamente debido a la sintaxis. Primero deben ser unificados a variables o escritos como cadenas de caracteres.

[ÍNDICE]

Datos simples y Estructuras de Datos: DOMINIOSEn cualquier lenguaje de programación necesitamos representar datos complejos. Podemos definir dato complejo como la agrupación homogénea o heterogénea de otros datos que a su vez pueden ser también complejos o simples. Así pues, a partir de esta definición, los datos se pueden organizar de múltiples formas que ya conocemos: vectores, registros, listas, árboles, grafos, etc.

Page 53: VP - TEMA 2

Un programa en Visual Prolog utiliza distintas secciones para las declaraciones de tipos o dominios con el fin de crear estructuras complejas de datos (DOMAINS), predicados (PREDICATES), reglas y hechos que forman los predicados (CLAUSES) y metas (GOAL).

En la sección de predicados se definen las cabeceras de los predicados que vamos a utilizar en nuestros programas, es decir el nombre del predicado y el dominio o tipo de los argumentos del mismo.

En la sección de cláusulas se define la implementación de cada predicado declarado en PREDICATES.

En la sección GOAL se establece la meta principal del programa.

Centrémonos ahora en la sección DOMAINS y en cómo crear estructuras complejas: objetos compuestos, árboles,  listas y matrices.

[ÍNDICE]

OBJETOS COMPUESTOS

Los objetos compuestos están formados por un functor y un conjunto de argumentos. Por ejemplo, cuadro(NS, Autor, Estilo, Dimensiones) representa una estructura que almacena los datos más relevantes para describir este tipo de obra de arte. El nombre cuadro es el functor y los elementos entre paréntesis son los argumentos del objeto compuesto. Los argumentos (campos), de un objeto compuesto pueden ser datos simples o datos complejos.

Para definir objetos compuestos es necesario introducirnos en el concepto de DOMINIO o DOMAINS. Como veremos en la sección de prácticas, la sección DOMAINS de un programa en Visual Prolog agrupa los dominios (tipos), no estándares en el sistema.

Definir un dominio no es más que establecer qué forma tendrán los objetos que pertenecen a dicho dominio o tipo, por ejemplo, para el caso del cuadro, el dominio sería así:

un_cuadro= cuadro(INTEGER, STRING, STRING, STRING)

Como hemos dicho, los argumentos de un objeto compuesto pueden ser complejos:

Page 54: VP - TEMA 2

un_comprador= comprador(STRING, un_cuadro)

El dominio o tipo del ejemplo define a un comprador de un cuadro. Los objetos compuestos de este tipo almacenan una cadena que puede representar el DNI y el cuadro que posee.

Veamos varios objetos de tipo un_comprador:

comprador("111111111", cuadro(1239, "Martín Jiménez", "óleo", "39x23")).

comprador("122111111", cuadro(1449, "Suárez Jiménez", "acuarela", "130x50")).

comprador("111133331", cuadro(2232, "Martín Pérez", "carboncillo", "100x100")).

[ÍNDICE]

ÁRBOLES

Un árbol es una estructura con una definición puramente recursiva, ya que se puede considerar como el elemento raíz cuyos hijos son, a su vez, árboles. Si el árbol tiene únicamente dos hijos se denomina árbol binario. Este modelo específico de árbol se utiliza mucho para resolver gran cantidad de problemas en aspectos de programación.

Un árbol se puede considerar, a su vez, un caso particular de grafo, donde todos los caminos son acíclicos.

La Figura 1 muestra un ejemplo de árbol.

Page 55: VP - TEMA 2

Figura 1

Ejemplo de árbol

Muchos problemas de Inteligencia Artificial donde interviene el concepto de búsqueda se resuelven mediante la implementación de árboles. Los árboles de juego o los utilizados en la resolución de problemas relacionados con el procesamiento de lenguaje natural son casos muy concretos y descriptivos. Por lo tanto, podemos notar que el manejo eficiente de estas estructuras es sumamente importante para conseguir programas de calidad en este ámbito de la Ciencia.

Ya vimos que Prolog es un lenguaje que se adapta adecuadamente a este tipo de problemas. De hecho la técnica de resolución que utiliza Prolog se basa en la construcción de un árbol de búsqueda de soluciones, luego podemos concluir que el conocimiento de esta estructura es clave para este tipo de metodología declarativa.

Como en cualquier lenguaje, lo que necesitamos saber es la forma de declarar el árbol, ya que su implementación se puede realizar de muchas formas, por ejemplo, aunque una lista es un caso particular de árbol, un árbol se puede representar a través de listas, aunque en el caso de Visual Prolog, estudiaremos la construcción de árboles utilizando objetos compuestos recursivos.

Dado que un árbol está formado por la raíz y un conjunto de hijos, podemos representarlo utilizando la siguiente notación:

oración (sujeto (artículo (el), sustantivo (hombre)), predicado (verbo (come), CD (pan)))

El árbol generado se muestra en la Figura 2.

Page 56: VP - TEMA 2

Figura 2

Árbol de análisis de una frase en español

Como se observa, el uso de un predicado con dos argumentos en el caso de árbol binario o N argumentos en el caso de árbol N-ario es una forma sencilla de representar un árbol.

Mediante objetos compuestos recursivos del tipo arbol(nodo, hijoizq, hijoder), donde hijoizq e hijoder son también árboles, podemos representar un árbol binario. El árbol vacío se representa a través del hecho vacio:

arbol(1,arbol(2,arbol(4,vacio,vacio),arbol(5,vacio,vacio)),arbol(3,vacio,vacio))

En la sección DOMAINS podemos crear un tipo árbol de enteros del modo siguiente:

mi_arbol= arbol(INTEGER, mi_arbol, mi_arbol); vacio

Operaciones con árboles representados mediante objetos compuestos recursivosdomains

arbol= nodo(integer, arbol, arbol); vacio

Page 57: VP - TEMA 2

lista= integer*

predicates

concatenar(lista, lista, lista)

preorden(arbol, lista)

inorden(arbol, lista)

postorden(arbol, lista)

clauses

concatenar([],[],[]):-!.

concatenar([],L2,L2):-!.

concatenar(L1,[],L1):-!.

concatenar([X|Y],L2,[X|Aux]):-concatenar(Y,L2,Aux).

preorden(vacio,[]):-!.

preorden(nodo(X,Izq,Der),[X|L]):-preorden(Izq,L1),

preorden(Der,L2),

concatenar(L1,L2,L).

inorden(vacio,[]):-!.

inorden(nodo(X,Izq,Der),L):-inorden(Izq,L1),

inorden(Der,L2),

concatenar(L1,[X|L2],L).

postorden(vacio,[]):-!.

postorden(nodo(X,Izq,Der),L):-postorden(Izq,L1),

postorden(Der,L2),

concatenar(L1,L2,L3),

concatenar(L3,[X],L).

goal

inorden(nodo(1,nodo(2,nodo(4,vacio,vacio),nodo(5,vacio,vacio)),nodo(3,vacio,vacio)), L1),

preorden(nodo(1,nodo(2,nodo(4,vacio,vacio),nodo(5,vacio,vacio)),nodo(3,vacio,vacio)), L2),

postorden(nodo(1,nodo(2,nodo(4,vacio,vacio),nodo(5,vacio,vacio)),nodo(3,vacio,vacio)), L3).

Page 58: VP - TEMA 2

[ÍNDICE]

LISTAS

Una lista se puede considerar como un caso particular de árbol del modo que se muestra en la Figura 3.

Figura 3

Lista implementada en forma de árbol

A su vez, una lista se puede considerar de forma recursiva. Es decir, siempre está formada por un elemento seguido de otra lista (ver Figura 4). Cuando la lista tienen un sólo elemento podemos considerar que está formada por dicho elemento y la lista vacía. Esta definición es muy interesante, ya que su conocimiento nos permitirá llevar a cabo todos las operaciones que se pueden realizar sobre las listas con poco esfuerzo.

Page 59: VP - TEMA 2

Figura 4

Definición recursiva de una lista

Hemos de recordar que en Prolog no existen estructuras para realizar bucles luego todo los algoritmos que representemos se definirán de forma recursiva. El hecho de que la implementación de la lista sea también recursiva facilita la construcción de operaciones sobre la misma.

Es necesario comprender este tipo de estructura para construir algoritmos eficientes. La mayor parte de las operaciones que se realizan sobre una lista implica un recorrido de la misma, luego hemos de centrarnos en conocer cómo se lleva a cabo este algoritmo utilizando técnicas de recursividad.

Una lista se puede especificar en un predicado o en un objetivo a través de:

una constante: [a, b, c, 1, pepe] una variable: L la estructura [Cabeza|Cola] que almacenará el primer elemento en la

variable Cabeza y el resto en la variable Cola.

Las listas pueden ser homogéneas o heterogéneas, es decir, almacenar elementos del mismo tipo, o elementos de distinto tipo.

Para definir un tipo de lista en particular es necesario declararla en la sección DOMAINS.

lista= elementos* (lista de una dimensión)

lista2= elementos** (lista de dos dimensiones)

lista3= elementos*** (lista de tres dimensiones)...

Es interesante observar la sintaxis de definición de una lista: elementos representa el dominio o tipo de los elementos que componen la lista.

El tipo elementos puede representar un dominio simple, es decir, sólo un tipo de elementos se corresponde con dicho dominio o un dominio complejo, donde varios tipos de elementos se corresponden con dicho dominio.

Por ejemplo, una lista homogénea de elementos simples estaría definida como:

Page 60: VP - TEMA 2

lista= integer*

Una lista heterogénea de elementos estaría definida como:

listaenteros=integer*

elementos= i(integer); s(symbol); c(char); le(listaenteros)

lista= elementos*

Ejemplo:domains

listaenteros= integer*

elementos= i(integer); c(char); s(symbol); le(listaenteros)

lista= elementos*

predicates

recorrer(lista)

clauses

recorrer([]):-!.

recorrer([X|Y]):-write(X), nl, recorrer(Y).

goal

recorrer([i(1),c('a'),s(pepe),i(5),c('b'),le([1,2,3])]).

 

Se observa que la lista puede contener cuatro tipo de elementos distintos: enteros, caracteres, símbolos y listas de enteros. En la sección de declaración del dominio o tipo elementos no podemos escribir:

elementos= integer; char; symbol; listaenteros

para expresar que dicho dominio agrupa a cuatro tipos de elementos distintos sino que la sintaxis a utilizar es aquella que representa que elementos agrupa a cuatro tipos de objetos compuestos distintos.

El resultado de la ejecución de la meta es el siguiente:

i(1)

Page 61: VP - TEMA 2

c('a')

s("pepe")

i(5)

c('b')

le([1,2,3])

yes

Las operaciones típicas que se pueden realizar sobre listas son: la inserción de elementos al principio, al final, en orden; borrado, búsqueda de elementos, recorrido, eliminación de duplicados y, en general, todas las de las que se pueden realizar sobre conjuntos de elementos tales como: intersección, unión, diferencia, pertenencia, comprobación de lista vacía, concatenación, etc.

Las listas se pueden utilizar para implementar otras estructuras tales como listas circulares, vectores, pilas, colas, árboles, grafos y matrices.

De cada estructura nos interesa saber cuáles son los algoritmos para acceder a ellas. Una vez que conocemos, perfectamente, el tipo de operaciones que las definen, cualquier tipo de implementación es válida. Por ejemplo, es frecuente usar una implementación mediante listas para plasmar matrices.

Por otro lado, es fundamental aplicar técnicas de diseño descendente para resolver todos nuestros problemas e implementar las estructuras necesarias en nuestras aplicaciones.

MATRICES

Podemos definir matrices a partir de listas, primero de 2 dimensiones y, más tarde, generalizar matrices de dimensión N.

En un lenguaje imperativo, recorrer una matriz de dos dimensiones implica el uso de un par de bucles anidados, que proporcionan una complejidad computacional O(n2). Sin embargo, en Prolog, no disponemos de este tipo de estructuras de control, por tanto, cualquier operación debe ser resuelta de forma recursiva mediante la declaración formal de su enunciado.

Un tratamiento elemento a elemento de las matrices tal y como se realiza en un lenguaje imperativo no es adecuado en Prolog, por tanto, conviene entender la estructura matriz como una lista de listas, y aplicar los algoritmos diseñados sobre listas para resolver problemas matriciales.

Page 62: VP - TEMA 2

Una matriz de cuatro dimensiones se puede ver como la secuencia de un conjunto de matrices de tres dimensiones, una matriz de tres dimensiones como un conjunto de matrices de dos dimensiones, una matriz de dos dimensiones como un conjunto o lista de matrices de una dimensión (vector o lista), por último, un vector o lista no es más que una secuencia de elementos simples.

Las definiciones dadas son recursivas, luego los algoritmos que debemos construir para realizar operaciones sobre este tipo de estructuras, así definidas, también serán recursivos.

Operaciones con matrices bidimensionalesdomains

fila=integer*

matriz= fila*

predicates

sumafila(fila, fila, fila)

sumar(matriz, matriz, matriz)

clauses

/*Predicado para calcular la suma de los elementos de una fila */

sumafila([],[],[]):-!.

sumafila([], L2, L2):-!.

sumafila(L1, [], L1):-!.

sumafila([C1|Cola1], [C2|Cola2], Res):- S=C1+C2,

sumafila(Cola1, Cola2, ColaRes),

Res=[S|ColaRes].

/*Predicado de recorrido de las filas para sumar los elementos mediante el uso del predicado anterior */

sumar([],[],[]):-!.

sumar([], L2, L2):-!.

sumar(L1,[], L1):-!.

sumar([C1|Cola1], [C2|Cola2], LR):-sumafila(C1, C2, Res),

sumar(Cola1, Cola2, ColaRes),

Page 63: VP - TEMA 2

LR=[Res|ColaRes].

goal

sumar([[1,2,3],[2,2,2],[4,4,4]],[[1,1,1],[2,1,2],[1,2,3]],R).

 

[ÍNDICE]

Bibliografía[Adarraga, 1994] Adarraga, Pablo. Zaccagnini José Luis. "Psicología e Inteligencia Artificial". Editorial Trotta. 1994.

[ÍNDICE]

Tutorial de visual prolog 7.3

Contenido

  [ ocultar ]

1   Conceptos

2   El Programa de

3   de diálogo no modal: Aspectos Conceptuales

4   El paquete FamilyData

5   Interfaces

6   El paquete FamilyDL

7   del Código para el paquete de familyDL

8   del Código para el paquete de familyBLL

9   Características de una capa de datos

10   Conclusión

11   Referencias

Conceptos

Hay un proverbio chino que dice así: "Dale a un hombre un pescado y lo alimentarás por un día Enséñale a pescar y lo alimentarás toda la vida.". Hay una lección en la programación de allí. A

Page 64: VP - TEMA 2

veces es mejor sustituir los datos ( un pescado ) con un método ( el procedimiento de la pesca ) que puede conducir a los datos.

Este concepto se puede ampliar aún más un poco más. Por ejemplo, si antes de enseñar la pesca persona directamente, el hombre se le enseña los principios de la obtención de alimentos de la naturaleza, entonces la persona tiene una mejor oportunidad de conseguir alimentos para su familia.

Pero aquí hay una advertencia: explicar las cosas con analogías puede ser un poco problemático, porque, cuando se estira una analogía demasiado lejos, se rompe! En el ejemplo anterior, si se le da al pescador una capa más , entonces él puede morir de hambre antes de que pueda cosechar los frutos de su buena educación!

Esta forma de conexión en cadena de reunir una serie de actividades para lograr el objetivo final que se puede hacer también en la programación. Pero entonces, esto tiene que hacerse de manera inteligente, de acuerdo con las exigencias que plantea la complejidad del problema de software que está tratando de manejar. Aunque se puede decir que más las capas que se introducen, mejor es el control al llegar a la meta final; se necesita para configurar las capas después de la previsión cuidadosa.

Si usted ve el patrón que ha evolucionado a través de toda la familia de la serie de tutoriales (Prolog fundamental Visual, Visual Prolog Fundamental - GUI, Visual Prolog fundamental - la capa de negocio lógico), se daría cuenta de lo mismo: las capas más y más refinadas eran presentación que finamente controlado el comportamiento del programa. Al mismo tiempo, no existían las capas innecesarias - si así fuera, sería sólo han aumentado la complejidad y la burocracia que conlleva el programa.

En este tutorial, vamos a introducir la capa de datos. El núcleo de la capa de datos no es más que una clase, y los objetos de esa clase se puede utilizar para acceder y configurar los datos reales. En el tutorial anterior, tenga en cuenta que la capa de lógica de negocios (BLL) maneja los datos también. Esta vez, el CSP se manejar solamente la lógica.

El Programa de

Pero en primer lugar, vamos a entender las distintas partes del programa con la ayuda del archivo family4.zip suministrado. En este tutorial, no estamos presentando el código que se necesita (cosa este tutorial será demasiado larga). Todo el código está presente en el ejemplo para el tutorial. Usted puede leer el código desde el IDE, cuando se carga el proyecto allí. Algunos parcial de código es, sin embargo, aquí presentes, para una referencia rápida.

Cuando se carga el proyecto ( family4 ) en el IDE de Visual Prolog, te darás cuenta de cuatro paquetes separados, como se muestra a continuación:

Page 65: VP - TEMA 2

Estos paquetes son los FamilyBLL, FamilyData , FamilyDL y paquetes TaskWindow. El último paquete (es decir, TaskWindow) se crea automáticamente por el IDE, cuando se crea el proyecto. El método para crear los otros paquetes se explica en un tutorial anterior.El proyecto fue creado de una manera similar que se explica en un tutorial anterior. No hay sorpresas allí. Pero aquí hay un poco de explicación de algo nuevo.

Hay una más de diálogo que se introduce en el TaskWindow paquete en comparación con el tutorial anterior. Este es elNewPersonDialog , que se muestra a continuación. (Ya hemos incluido este cuadro de diálogo en el proyecto. Sin embargo, la explicación a continuación pretende que se está creando este cuadro de diálogo por su cuenta en el proyecto.)

Este diálogo es un diálogo modal, que se utiliza para recoger la información de una nueva persona que va a ser insertado en la base de datos actual. Como se puede ver en la imagen de arriba, hay un campo de edición de texto (idc_name) para recoger el nombre, una serie de botón de radio (idc_male y idc_female) para encontrar el género, y otros dos campos de texto de edición (idc_parent1 y idc_parent2) a encontrar a los padres de la persona que se añade a la base de datos. Un cuadro de diálogo modal es uno, que puede permanecer abierta y activa, sin interferir en el funcionamiento de otras partes de la interfaz gráfica de usuario.No hay ninguna diferencia entre un modal y no modal de diálogo cuando se está creando. Sin embargo, al establecer sus atributos que debe tener cuidado de especificar que es de hecho un modal uno, como se muestra a continuación:

Page 66: VP - TEMA 2

Ahora sólo tienes que crear un diálogo no es de ninguna utilidad si no hay manera por la cual se puede invocar desde el cuadro de diálogo TaskWindow. Por lo tanto tenemos que insertar un nuevo elemento de menú en eso y asociar un controlador de eventos con ese elemento de menú. Una vez que se crea el elemento de menú correspondiente, el asistente de código que nos permitirá crear un controlador en el que podemos introducir el código requerido. La imagen se muestra a continuación:

Page 67: VP - TEMA 2

Tenga en cuenta que en un tutorial anterior (Prolog fundamental Visual - GUI), que había mostrado cómo crear un controlador de eventos para un elemento de menú mediante el asistente de código anterior.

Modal de diálogo: Aspectos Conceptuales

Este programa utiliza un cuadro de diálogo modal con el fin de insertar la información relativa a las personas nuevas en lafamilia de base de datos. Como se indica en un tutorial anterior, la codificación de los acontecimientos que suceden a partir de un diálogo no modal a veces puede ser complicado. Esto se puede entender si se sabe exactamente por qué un cuadro de diálogo modal funciona de la manera que lo hace. En un cuadro de diálogo modal, el sistema operativo suspende los acontecimientos que suceden en todas las otras partes de la interfaz gráfica de usuario de la misma aplicación, y se concentra sólo en las que surgen del cuadro de diálogo modal. La lógica necesaria para ser manejados por el programador en ese punto en el tiempo se vuelve bastante simple, ya que el programador está muy seguro de que no hay eventos adversos que ocurren de manera simultánea en otras partes del mismo programa.

Por otro lado, cuando un diálogo modal está en uso, el programa es receptiva a los acontecimientos, incluso de las partes del programa , junto con los que se producen desde el diálogo dijo. Las permutaciones y

Page 68: VP - TEMA 2

combinaciones de control de eventos necesidad requerida para ser cuidadosamente analizados por el programador. Por ejemplo, el programa podría abrir una base de datos, y luego tener un modal de diálogo abierto, el cual trabajó en la base de datos. Se lanzaría un error, cuando el diálogo se pone en uso, si en el fondo de la base de datos se cerró sin cerrar el cuadro de diálogo. A menos, claro, que había previsto un curso de los acontecimientos y programado para este tipo de situaciones.

Si ve el código fuente que se ocupa de los acontecimientos que suceden a partir de este diálogo en NewPersonDialog.pro, verá que el código cuidadosamente las capturas de una gran cantidad de situaciones de error. Incluso tiene un verificador de integridad que intenta rastrear lo que puede hacer que un pedazo de código para provocar un error.

Los predicados que pueden conducir a errores en tiempo de ejecución se llaman desde dentro de un especial integrado en el predicado, llamado trap . Por ejemplo, véase el siguiente código:

idc_name ) ) , Nombre "" , MaleBool = vpi :: winIsChecked ( VPI :: winGetCtlHandle ( thisWin, tratar , ! , stdio :: write ( "insertado" , Nombre , " \ n " ) . onControlOK ( _Ctrl , _CtrlType , _CtrlWin , _CtrlInfo ) = manejado ( 0 ) .En el código anterior, el programador no está seguro de si la adición de una persona en la base de datos es realmente posible (porque no se permite la incorporación de una persona ya existente). Así, el addPerson no se llama directamente, sino a través de la trampa de predicado. El predicado de la trampa acepta tres variables: la primera de las cuales es el predicado de ser llamados a hacer el trabajo real en ese punto en el código, el segundo es el número de error que se genera en el caso se activa un error, y es la tercera un predicado que se llama en la activación del error. Ese predicado tercera puede ser utilizado por el programador para que el programa de forma segura de nuevo en el control.

El paquete de FamilyData

En este ejemplo, vamos a crear un paquete de FamilyData, que tiene todo el código necesario para el manejo de los dominios que se necesitan por el programa. Los mismos dominios estaría disponible para el plomo en sangre (la capa de lógica de negocios), cuando se comunica con la capa de datos.

Page 69: VP - TEMA 2

En esta clase, vamos a escribir el siguiente código:

clase familyData abierto central dominios de género = femenino ( ) , macho ( ) . dominios persona = cadena. person_list = persona * . dominios optionalPerson = noPerson ( ) ; persona ( persona Persona ) . predicados toGender : ( cadena de GenderString ) -> de género de género . predicados toGenderString : ( de género de género ) -> string GenderString . predicados ClassInfo : núcleo :: ClassInfo . fin de la clase familyDataSi te fijas, la clase anterior contiene las definiciones de dominio que van a ser compartidos entre varias clases. Por lo tanto, el propósito de este paquete es actuar como un lugar de reunión entre la capa de datos y la capa de negocios .

Interfaces

La capa , que se encargará de los datos, será a través de los objetos creados a partir de una clase en el paquete de FamilyDL.Sin embargo, antes de llegar a los contenidos reales de dicho paquete, tenemos que entender un concepto llamado de una interfaz .

Una interfaz puede ser visto como una declaración de intenciones sobre qué esperar en los objetos que se adhieren a la interfaz, dijo. Está escrito en un archivo separado utilizando el IDE y puede contener predicados que se pueden esperar ser encontrado en los objetos de una clase.

Una interfaz también puede ser utilizado para las definiciones de algunos más que no se explican en este tutorial. Usted puede aprender sobre el uso de los avance en un tutorial futuro.

Page 70: VP - TEMA 2

La razón para escribir las intenciones por separado en una interfaz diferente es una una elegante: Una clase puede ser escrito para adherirse a las declaraciones se encuentran en una interfaz. La interfaz ofrece una definición abstracta de la funcionalidad proporcionada por los objetos de la clase. La definición es "abstracto", ya que sólo expresa parte de la funcionalidad de los objetos. En resumen, una clase de forma declarativa se puede afirmar que los objetos se comportan de acuerdo a la interfaz.

Varias clases pueden implementar la misma interfaz, cada uno de ellos proporcionando un específico y concreto (es decir, no abstracto) la aplicación de la abstracción. Esto reduce la carga de mantenimiento del código enormemente. Más tarde, cuando más sofisticación se va a introducir en el mismo programa, el programador / s puede trabajar en estas interfaces y luego trabajar su camino a partir de ahí metódicamente.

El paquete de FamilyDL

Este ejemplo también contiene otro paquete llamado el paquete FamilyDL. Este paquete contiene la clase principal, cuyos objetos se ocuparía de los datos reales.

Este paquete contiene una interfaz - la interfaz familydl. El propósito principal de este interfaz es definir los predicados que son proporcionados por la capa de datos a la capa de lógica de negocios. Como tal, juega un papel importante en la capa de datos. Esta interfaz se basa en los dominios del paquete FamilyData.

interfaz de familyDL abierto central, familyData predicados person_nd : ( persona Nombre ) nondeterm ( o ) . predicados de género : ( persona que nombre ) -> de género de género . predicados parent_nd : ( persona persona , persona de Padres ) nondeterm ( i, o ) . predicados addPerson : ( persona persona , el género de género ) . predicados addParent : ( persona persona , persona de Padres ) .

Page 71: VP - TEMA 2

 predicados personExists : ( persona Persona ) determ . predicados salvar a : ( ) . finales de interfaz familyDLEn el código anterior, la palabra clave abierto se utiliza para indicar que las declaraciones tanto en el núcleo y los paquetes familyData se utilizan en esta interfaz.

El paquete contiene dos clases: family_exception y family_factfile. La mayor parte del trabajo será realizado por family_factfile .Ese archivo contendrá el código, lo que trae en el pescado (si se fuera a utilizar de nuevo la metáfora del proverbio chino se mencionó al principio de este tutorial). Se crea sistemáticamente la base de datos que es utilizado por el programa, y, al hacerlo, así que comprueba para cualquier situación de error que puede ocurrir. En caso de que haya un error, sería utilizar los predicados de la clase family_exception para señalar el error de forma sistemática.

Código para el paquete de familyDL

El paquete tiene una interfaz de familyDL familydl.i archivo, el código que se le dio antes. El paquete también contiene dos archivos. Profesionales que contienen las implementaciones de los dos. Cl (clase) de los archivos. Las dos clases de este paquete son familydl_exception y familydl_factfile. El código de familydl_factfile puede examinarse desde el archivo family4.zip que se puede descargar por separado para este tutorial. Los predicados de ese archivo son para la manipulación de los datos, y hemos cubierto que en nuestro tutorial anterior.

El código de familydl_exception tampoco se presenta aquí en este tutorial. Por favor, lea desde el archivo family4.zip. Los predicados contienen predicados de apoyo de servicios públicos que se utilizan para mostrar los mensajes correctos de error, cuando los datos incorrectos y / o errores (conocidos como excepciones En informática) son capturados por el programa.Muchas de estas situaciones de error ocurrir sin el control del programador (por ejemplo, una puerta de la unidad de ser abierta es una excepción que no se puede predecir), pero a veces, se requiere para hacer un error de pasar. Esto significa que el programador de forma deliberada genera un error (para usar la jerga informática aquí). Así, la utilidad de los predicados de error empezar con el término raise_ como usted puede notar en el código.

Nótese que familydl_exception no contiene la lógica para la manipulación de las excepciones sí mismos. Son predicados de servicios públicos que

Page 72: VP - TEMA 2

permiten que el programa muestre el cuadro de diálogo de error correcto, cuando las excepciones son a suceder.

Aquí está un ejemplo de cómo estos predicados raise_ deliberadamente invocado por el programador para señalar una situación de error.

En el familydl_factfile.pro usted encontraría un predicado de la adición de un padre. En el cuerpo cláusula, en primer lugar, pasa a través de dos cheques para ver si tanto a la persona, así como el padre de la persona son los actuales. Si alguno de ellos no existe, entonces familyDL_exception :: raise_personAlreadyExists se llama. Esto plantea la excepción y el programa mostrará un cuadro de diálogo especial de error en ese punto. Afortunadamente, ese cuadro de diálogo de error en particular no tiene que ser construido por el programador. El trabajo de creación de diálogo se lleva a cabo por las bibliotecas de Visual Prolog cuando se vincula el programa. No sería la creación de un diálogo de error por separado la forma en que lo hizo para otros diálogos.

% Nota: El siguiente es sólo parcial, el código% a partir de familydl_factfile.pro cláusulas addParent ( Persona , padre ) : - check_personExists ( Persona ) , check_personExists ( Padres ) , los assertz ( parent_fct ( Persona , los padres ) ) . cláusulas personExists ( Persona ) : - person_fct ( Persona , _ ) y ! . predicates check_personDoesNotExist : (string Person).clauses check_personDoesNotExist(Person) :- personExists(Person), !, familyDL_exception :: raise_personAlreadyExists ( ClassInfo, Persona ) . check_personDoesNotExist ( _ ) . ...

Código para el paquete de familyBLL

El código de la familyBL.i archivo de interfaz es la siguiente. Esto es muy similar a la interfaz de la capa de negocio que fue desarrollado para el

Page 73: VP - TEMA 2

tutorial anterior. Pero hay diferencias importantes: los dominios se han desplazado fuera de este paquete y ahora son comúnmente compartidos entre la capa de datos y la capa de negocio. Este es un paso obvio, porque en el tutorial anterior que había mantenido tanto la capa de negocio y la capa de datos en una sola pieza de código. Al separar estas dos capas, que todavía se necesitaría algún punto en común a través del cual estas dos capas se puede hablar el uno al otro, y la decisión obvia sería hacer que los dominios utilizados por ellos comunes a los demás.

El segundo cambio que se puede notar es que los predicados no tiene varios de los flujos. Cada predicado hace exactamente una cosa con los parámetros que maneja. Esto hace que el programa se mueven lejos del tradicional prólogo y se convierte en similar al de los programas escritos en otros idiomas. A menudo, esta estrategia conduce a mejores resultados, ya que hace más fácil entender el programa cuando uno lee el código fuente. Sin embargo, el método anterior de tener múltiples flujos de hace resultado en el código fuente más corta y es a menudo preferido por los programadores que vinieron después de usar en el prólogo tradicional.

interfaz de familyBL abierto central, familyData  predicados personWithFather_nd : ( persona persona , persona Padre ) nondeterm ( O, O ) . predicados personWithGrandfather_nd : ( persona persona , persona Abuelo ) nondeterm ( O, O ) . predicados ancestor_nd : ( persona persona , persona que los antepasados ) nondeterm ( i, o ) . predicados addPerson : ( persona persona , el género de género , optionalPerson Parent1 , optionalPerson parent2 ) . predicados salvar a : ( ) . finales de interfaz familyBLEl código fuente para el familyBL clase no se presenta aquí. Usted puede leer una vez que se carga el código del ejemplo suministrado en el IDE. No debe haber sorpresas. Lo hace las mismas actividades que se llevaron a cabo en la capa de negocio del tutorial anterior (Prolog visual fundamental - la capa de negocio lógico). Sin embargo, hay dos diferencias importantes:

Page 74: VP - TEMA 2

allí donde el acceso a datos o la creación de los datos que se necesita, los predicados de la clase familyDL se utilizan. Además, los predicados de esta clase de comprobar la validez de la información y crear excepciones en el caso de los errores lógicos se encuentran.

El paquete contiene una clase más: familyBL_exception . Esta clase es similar a la que nos encontramos en la capa de datos.Pero esta vez, contiene los predicados de servicios públicos que se utilizan para crear excepciones, cuando los predicados de capa de negocio está en el trabajo.

Características de una capa de datos

La característica principal de una capa de datos, es que implementa un conjunto de predicados referidos colectivamente como los descriptores de acceso de datos en lenguajes orientados a objetos. Un acceso de datos desacopla de acceso a datos de la aplicación subyacente. En términos más simples, se utiliza predicados a establecer y obtener los datos utilizados por el programa. Los datos reales se mantendría privado e inaccesible desde el exterior.

De acceso de datos también puede proporcionar una interfaz uniforme para acceder a los atributos de los datos, dejando la organización concreta de los atributos ocultos.

La ventaja de utilizar de acceso de datos es que los algoritmos implementados en otras áreas del programa puede ser escrito independiente del conocimiento sobre la estructura de datos exacta que se utilizó en la capa de datos. En el siguiente tutorial de esta serie, se vería que los datos pueden ser fácilmente trasladado a un sistema más sólido (con la base de datos de Microsoft Access, acceso a través de una capa ODBC), manteniendo todo lo demás exactamente lo mismo.

Casi no hay obstáculos a este enfoque, pero cualesquiera que sean, estas trampas debe ser entendido. La principal es que los dos niveles más sofisticados de entendimiento tiene que ser alcanzado por el programador al escribir la capa de datos .Una de ellas sería para determinar las estructuras de datos internas que se mantendrá en privado dentro de la capa de datos.(El tipo de pensamiento, necesarios para desarrollar las estructuras de datos, se discutió en el tutorial de Visual Prolog fundamental en relación con funtores). Pero después de eso, el programador también tendrá que pasar tiempo en el diseño eficiente de acceso de datos a fin de que los predicados de las otras capas son capaces de reproducirse sin problemas con lacapa de datos . El diseño incorrecto de los datos de acceso predicados a menudo pueden crear un montón de dolores de cabeza, cuando un equipo de programadores es en el trabajo. Sobre todo, cuando el programa se requiere para pasar de una simple a una más compleja ... como usted se daría cuenta en el siguiente tutorial.

Page 75: VP - TEMA 2

Conclusión

En este tutorial se utilizó una estrategia de " enseñar a la pesca del programa y no darle el pescado "en nuestro programa. Esto no es sino modularización del código fuente del software inteligente para que el mantenimiento del código y la extensión del código se convierte en extremadamente simple. Nos separamos de la capa de negocios antiguo del tutorial anterior en dos partes: una lógica contenida negocio puro y el otro maneja sólo los datos. Esto nos permite extender el programa más tarde y simplemente reemplazar la capa de datos con otras formas de técnicas de manejo de datos (por ejemplo, a través de los datos obtenidos a través de un protocolo ODBC, oa través de Internet, etc) Este tutorial también el manejo cubierta excepción y descubrimos que , lo fácil que es escribir nuestra propia excepción elevar los predicados, y las usará para mostrar mensajes de error inteligentes para el usuario.