Sie sind auf Seite 1von 53

Apuntes del Tema 2

Desarrollo de SBCs en Clips.

1.INTRODUCCIÓN
El objetivo de estos apuntes es que el alumno adquiera los fundamentos de los
sistemas basados en el conocimiento (SBCs) y de su programación sobre los
paradigmas de reglas y objetos. Que el alumno aprenda hacer uso de estos
paradigmas y aprovechar sus capacidades en las aplicaciones más adecuadas y en
aquellas áreas para las que realmente tiene sentido. Para ello se toma como soporte
el entorno de desarrollo sobre los lenguajes Clips y COOL.
Para cualquier duda respecto a un comando o palabra reservada lo mejor es
acudir a la documentación “oficial” de Clips en:
http://clipsrules.sourceforge.net/OnlineDocs.html
Índice:
1. Introducción a Clips: Estructuración, módulos, templates, facts, rules, etc.
Agenda. Secuenciación. Encadenamiento hacia adelante.
2. Primeros pasos prácticos en Clips. Entorno de desarrollo. Componentes del
lenguaje. Cláusulas básicas. Ejercicios con reglas y comparación de patrones.
Entrada y salida de datos. (taller, 1,5 hora)

1
Tema 2. Desarrollo de SBCs en Clips

3. Mantenimiento de la verdad. Prioridad y control. La agenda. Más ejercicios.


(taller, 1,5 hora)
4. Uso de los módulos. Problemas más complejos. Diagnóstico y clasificación.
(1 hora)
5. Programación procedimental en Clips. Programación orientada a objetos en
Clips: COOL. Algunos ejercicios. (taller, 1 hora)

6. Una forma de tratar la incertidumbre en Clips, factores Mycin. (taller, 1,5


hora)
7. Un motor de encadenamiento hacia atrás. (taller, 1,5 hora)

1. Introducción a Clips.
Desarrollada por Software Technology Branch de la NASA/Lyndon B. Johnson
Space Center
Características:
- 3 paradigmas de programación:
* programación basada en reglas: reglas+ hechos (facts)
* programación procedural: funciones
* programación orientada a objetos (COOL): objetos+paso de mensajes
- El mecanismo de inferencia básico es el encadenamiento hacia adelante.
- CLIPS es una herramienta escrita en C
- Plena integración con otros lenguajes como C y ADA
- Desde un lenguaje procedural se puede llamar a un proceso CLIPS, éste
realiza su función y luego le devuelve el control al programa.
- Se puede incorporar código procedural como funciones externas en CLIPS
- Las reglas pueden hacer "pattern-matching" sobre objetos (COOL) y facts
formando así un sistema integrado.
- CLIPS tiene una sintaxis estilo LISP que utiliza paréntesis como
delimitadores.

2
Apuntes de Ingeniería y Gestión del Conocimiento

- Tiene una versión en Java denominada Jess) que es muy usada y una versión
con lógica difusa fuzzy denominada FuzzyClips (y FuzzyJess).
- Dispone de un entorno de trabajo desde el que se puede trabajar con un
terminal, línea de comandos, y un sistema de menús, que básicamente lanzan
comandos: La ayuda del programa permite tener en línea la descripción completa
de cada comando y cada elemento del lenguaje, por lo que en este documento no se
necesita ser exhaustivo en estas particularidades.

• Comandos del sistema: Se introducen entre paréntesis, a continuación del


prompt de comandos y se ejecuta con la introducción de [enter]. Ejem:

> (+ 5 9)
> 14
>
Los comandos, aparte de introducirse directamente en la línea de comandos del
sistema, pueden agruparse en un fichero batch y ejecutarse como tal. Un programa
Clips será un fichero de este tipo con comandos para definir construcciones. Desde
el entorno se lanza este programa y se monitoriza su ejecución.
Para esto último hay comandos específicos. En particular el comando watch
permite visualizar las modificaciones de uno o unos elementos determinados del
lenguaje o del programa. El argumento del comando es la referencia al elemento o
elementos que se desean observar. Puede ser específico, una instancia o una regla
(en este caso se observarían sus activaciones) determinadas, por ejemplo, o
genérico, todos los hechos, también por ejemplo. Para esto último, el argumento
debería ser la denominación de los elementos genéricos que se irán referenciando
en este apartado: facts, rules, instances, activations, etc. El comando inverso es
unwatch.
Así el entorno permite definir:

• Construcciones: Hablaremos de construcciones, como elementos que soportan


las clases, instancias (los hechos) y las reglas. Pueden introducirse directamente
en la línea de comandos o a través de un fichero texto que se carga desde el
entorno.

Y controlar la:

3
Tema 2. Desarrollo de SBCs en Clips

• Ejecución: Si en la base de hechos hay hechos que cumplen las restricciones


de la parte izquierda de una regla, ésta entra en la agenda. Cuando se lanza
(activation en terminología Clips) una regla, se ejecutan las acciones de su parte
derecha. Al ejecutar un programa, se lanza primero la regla contenida en la
agenda que tiene una mayor prioridad.

Con (run) se ejecutan todas reglas.


El comando (agenda) muestra las reglas contenidas en la agenda, su
prioridad y los hechos que han cumplido su parte izquierda.
Clips tiene la propiedad denominada refracción, esto es, ante un conjunto
de hechos determinado, las reglas solamente se disparan una vez. Sino
fuera así, se caería inmediatamente en una repetición constante de las
mismas reglas. Clips “recuerda” los identificadores de los hechos que
desencadenaron una regla en el disparo y no activará nuevamente la regla
con la misma combinación exacta de identificadores. Hay que tener en
cuenta que cuando se retira y se vuelve a introducir un hecho, su
identificador ha cambiado y por lo tanto puede volver a disparar la misma
regla. El comando (refresh nom-regla) permite que la regla referenciada se
vuelva a disparar aún sin haber cambiado los hechos participantes.

2. COMPONENTES DEL LENGUAJE

• Hechos:

Los hechos representan variables, propiedades y valores que éstas toman.


Son fragmentos de información compuestos por un número ilimitado de
componentes (campos) separados por blancos y encerrados entre paréntesis
balanceados. Los campos pueden tiene o no un nombre asignado (que en
definitva es un campo más). Ej. (luis 3), (pedro (altura 180)), (pedro altura
180). (altura pedro 180)

Todos los hechos tienen un identificador (una clave que genera el sistema)
por el que puede ser referenciado.

4
Apuntes de Ingeniería y Gestión del Conocimiento

CLIPS define por defecto un hecho inicial (initial-fact) cuando se carga la


aplicación.

Todos los hechos se insertan en la lista fact-list. Se puede ver esta lista con
el comando (facts)

Tipos de Hechos: Hechos ordenados:

• son básicamente listas

• los campos no tienen nombre asignado y pueden ser de cualquier tipo


primitivo de datos, excepto el primero, que debe ser un símbolo

• el orden de los campos es significativo

• tipos de los campos: float, integer, symbol, string, external-address, fact-


address, instance-name, instance-address

Ejemplo: (Antonio 45678CLZ conductor Barcelona)

(Antonio 45678 conductor Barcelona)

Hechos compuestos: indican diferentes hechos particulares sobre una misma


relación. Por ejemplo: (Empleado (nombre Antonio) (id 45678) (puesto
conductor) (ubicacion Barcelona))

Clips permite definir plantillas para (posteriormente) poder definir hechos con esa
estructura común:

Templates o plantillas: Definen la estructura de hechos compuestos, lo que en


orientación a objetos sería la definición de clases.
(deftemplate vehiculo
(slot matricula)
(slot color)
(slot propietario)
(slot antiguedad)
(slot cilindrada)
(slot kilometraje))

5
Tema 2. Desarrollo de SBCs en Clips

Los slot pueden ser simples (slot) o slots múltiples (multislot), esto es, que
pueden contener varios valores (campo multivaluado).
En el ejemplo anterior de template no se ha definido el tipo de datos de slot y
por lo tanto esos slots admiten cualquier tipo de datos, pero esto se puede restringir
a través del operador type. En el ejemplo:
(deftemplate vehiculo
(slot matricula (type SYMBOL))
(slot color (type SYMBOL))
(slot propietario (type STRING))
(slot antiguedad (type INTEGER))
(slot cilindrada (type INTEGER))
(slot kilometraje(type INTEGER)))
Los identificadores del tipo de datos son: SYMBOL, STRING, LEXEME
(válido para símbolos y strings), INTEGER, FLOAT, NUMBER (válido para
enteros y reales) y ?VARIABLE, este último permite cualquier valor y es la
restricción por defecto, como hemos visto.
Hay otras posibilidades para la restricción de datos en los slots. Por ejemplo los
operadores que definen por enumeración los valores permitidos, que se indica con
el operador allowed-tipo-datos, donde tipo-datos pude ser symbols, strings, lexems,
integers, floats, numbers o values. Por ejemplo:
(deftemplate vehiculo
...
(slot color (type SYMBOL)(allowed-symbols verde rojo azul))
...

Una nota importante, si elimináramos del ejemplo anterior la opción type:


(deftemplate vehiculo
...
(slot color (allowed-symbols verde rojo azul))
...
el slot color admitiría cualquier valor de otros tipos, pero si es un símbolo, éste
debería ser uno de los enumerados. Para restringir específicamente a los
enumerados, utilizaríamos la variante con values:
(deftemplate vehiculo
...
(slot color (allowed-values verde rojo azul))
...

Para restringir valores numéricos es más habitual definir un rango. Para ellos se
dispone, lógicamente, de range:
(deftemplate vehiculo
...

6
Apuntes de Ingeniería y Gestión del Conocimiento

(slot antiguedad (type INTEGER)(range 0 ?VARIABLE)))


...
El ejemplo restringe los valores
de antigüedad a valores superiores o Facetss: Son características que ayudan en el desarrollo y
iguales a 0. En 0 es el valor mínimo mantenimiento de un SE proporcionando un fuerte tipado y
del rango y el valor máximo no comprobación de restricciones.
queda definido, para esto se ha type
utilizado ?VARIABLE.
(type <especificación-tipo>)
Para los slots multivaluados, se
- Define el tipo de datos que puede tener el slot.
dispone de la posibilidad de definir
el número mínimo y máximo de - Los tipos válidos son: SYMBOL, STRING, LEXEME,
valores admisibles, a través de INTEGER, FLOAT, NUMBER. - Si se especifica ?VARIABLE,
significa que el slot puede tomar cualquier tipo de dato (por
cardinality. Si queremos determinar
defecto, se supone este).
la cardinalidad del multislot, no a
un rango, sino a un único valor, - LEXEME equivale a especificar SYMBOL y STRING
conjuntamente,
indicaríamos ese valor como límite
mínimo y máximo a la vez. En el - NUMBER equivale a INTEGER y FLOAT.
siguiente ejemplo indicamos que la (deftemplate persona
matricula de un vehículo se
compone exactamente de tres (multislot nombre (type SYMBOL))
strings. (slot edad (type INTEGER))
(deftemplate vehiculo
... )
(multislot matricula (type string)(cardinality 3 3))
...

Por supuesto también se dispone de la posibilidad de dar un valor por defecto al


slot mediante el atributo default. Si no se incluye este atributo o se incluye con
argumento ?DERIVE, al afirmar ese slot sin darle un valor, el sistema obtiene un
valor a partir de su tipo de datos y de su rango y cardinalidad. Por ejemplo:
(deftemplate conduccion
(slot conductor-principal (type STRING))
(slot tipo-permiso (allowed-values A-1 A-2 A-3))
(slot permiso-valido (default TRUE))
(slot edad (type INTEGER)(range 18 ?VARIABLE))
(multislot otros-conductores (type STRING) (cardinality 2 3))
...
Recordemos que el template es solo una estrucutra no un hecho, es decir que
hace falta crear (afirmar) el hecho indicando que se ajusta a ese template. Si
afirmamos sin más:
(assert (conduccion))

7
Tema 2. Desarrollo de SBCs en Clips

Obtendríamos en la base de hechos el siguiente hecho:


(conduccion
(conductor-principal "")
(tipo-permiso A-1) Restricciones: allowed-
allowed-symbols, allowed-strings, allowed-lexemes,
(edad 18)
allowed-integers, allowed-floats, allowed-numbers y allowed-
(otros-conductores "" "") values.
... Los atributos allowed- no restringen el tipo del slot. En el
ejemplo,
Ya que obtiene el primer valor de un allowed-symbols no restringe el tipo del slot sexo a ser un
allowed-values, el mínimo de un range, símbolo. Sólo indica que si el valor del slot es un símbolo,
el 0 de un INTEGER, "" si es STRING entonces debe ser el símbolo hombre ó el símbolo mujer. En el
y el valor nil si no hay especificación de ejemplo, cualquier string, entero o real sería legal para el slot
sexo si no se especificara (type SYMBOL).
ningún tipo. En un multislot, se
(deftemplate persona
obtendrían tantos valores como el valor (multislot (nombre (type SYMBOL))
mínimo de su cardinalidad. (slot edad (type INTEGER))
(slot sexo (type SYMBOL)( allowed-symbols hombre
Como hemos visto en el ejemplo, la mujer))
referencia a un hecho se hace con el )
siguiente formato: (nombre-template
range (range <valor-min> <valor-max>)
(valor valor)).
cardinality (cardinality <valor-min> <valor-max>)
Para visualizar hechos en un instante (slot edad (type INTEGER))
)
desde la línea de comandos (facts), cuyo
resultado es una lista de hechos y su
Restricciones: default
número asignado. Para incluir un hecho (default <especificación>)
se utiliza assert: (assert hecho-1 hecho-2 Permite especificar un valor por defecto para un slot cuando
... hecho-n). Para suprimirlo, retract: no se le ha dado valor en la afirmación del hecho.
(retract num-hecho). Para modificarlo, (deftemplate ejemplo
(slot a) ;;; el tipo se supondrá SYMBOL
se utiliza (modify num-hecho (slot
(slot b (type INTEGER))
valor)+), que suprime el antiguo hecho (slot c (allowed-values rojo verde azul))
y añade el nuevo. La función duplicate (multislot d) ;;; por defecto contendrá el valor ()
añade también el mismo hecho pero no (multislot e (cardinality 2 2)
elimina el antiguo. (type FLOAT)
(range 3.5 10.0)))
Para definir hechos de forma
Al afirmar un hecho sin dar ningún valor a ninguno de sus
automática a través de un programa, se slots, el hecho realmente contendría lo siguiente:
puede utilizar deffacts que se
construyen con esta sintaxis (deffacts (ejemplo (a nil) (b 0) (c rojo) (d) (e 3.5 3.5))
nombre comentario lista-de hechos). En
orientación a objetos hablaríamos de
instancias.
(deffacts vehiculos-vendidos
(vehiculo (matricula MIB-3456-ERC)
(color rojo)

8
Apuntes de Ingeniería y Gestión del Conocimiento

(propietario "Luis Gomez"))


(vehiculo (matricula BBB-6765-IXC)
(color verde)
(propietario "Jose Perez")))
Se entiende por hecho toda la instancia de la construcción template, de tal forma
que el siguiente hecho es imposible
(vehiculo (matricula MIB-3456-ERC)
(color rojo)
(propietario "Luis Gomez"))
y en todo caso:
(vehiculo
(matricula MIB-3456-ERC)
(color rojo)
(propietario nil)
(antiguedad nil)
(cilindrada nil)
(kilometraje nil))

Reset elimina todos los hechos y afirma todos los hechos especificados por los
deffacts.
El comando (assert hecho) añade el hecho a la base de hechos. Desde luego si
ese hecho ya estaba en la base, no tiene efecto la adición. Pero hecho debe ser la
descripción completa del hecho, puesto que en caso de no describirse unos
determinados slots, estos se consideran como nil. Así si afirmamos:
(assert (vehiculo
(matricula MIB-3456-ERC)))
El resultado en la base de hechos es la inclusión de:
(vehiculo
(matricula MIB-3456-ERC)
(color nil)
(propietario nil)
(antiguedad nil)
(cilindrada nil)
(kilometraje nil))

El comando retract: (retract <fact-index>*) elimina el hecho/s en la Base de


Hechos cuyo índice es <fact-index>
El comando modify (modify <fact-index>* (slot new-value)) permite modificar
el hecho, cambiando el anterior valor del slot slot por new-value.
modify: (modify <fact-index>* (slot new-value)) modifica el hecho

9
Tema 2. Desarrollo de SBCs en Clips

3. REGLAS Y COMPARACIÓN DE PATRONES

• Reglas: tienen la siguiente sintaxis: (defrule nombre-reg comentario patrones


=> acciones).
Defrule (defrule <nombre-regla> [<comentario>]
<elemento-condición>* ; Parte izquierda (LHS)
Evidentemente “patrones” define las =>
restricciones exigidas para que se lleven a cabo <acción>*) ; Parte dcha. (RHS) de la reglaparámetros de
las “acciones”. Si no existe un solo patrón en la funciones ni métodos
regla, ésta se lanzará cada vez que se ejecuta el (defrule evaluar-hallazgos-V1
(datos-paciente (id ?paciente) (edad ?edad&:(> ?edad 65)))
comando reset.
?hallazgos <- (hallazgos-paciente (id-paciente ?paciente)
Las variables para la comparación de (hallazgos $?lista-hallazgos))
=>
patrones guardan semejanza en funcionalidad y (modify ?hallazgos (hallazgos V1))
sintaxis con las de prolog. En el siguiente )
ejemplo , la regla buscará los vehículos de color
verde:
(defrule encontrar-vehiculo-de-color
(vehiculo (matricula ?matricula) (color verde))
=> (printout t "El vehiculo " ?matricula " tiene el color verde" crlf))
En la siguiente variante de esta regla, se utiliza un slot (encontrar color) que
contiene el color que se busca.
(deftemplate encontrar (slot color))
...
(defrule encontrar-vehiculo-de-color
(encontrar (color ?color))
(vehiculo (matricula ?matricula) (color ?color))
=> (printout t "El vehículo " ?matricula " tiene el color " ?color crlf))

Así, con los siguientes dos comandos nos


listarían las matrículas de vehículos de color Se deben verificar cada una de los ECs. Para ello:
verde: 1. Para ello deben existir los hechos especificados en las ECs
> (assert (encontrar (color verde))) 2. Se deben verificar las expresiones sobre ellos.
> (run) Una regla se ejecuta cuando:
1. todos sus ECs son satisfechos por la base de hechos
En el ejemplo anterior se buscaban los 2. el motor de inferencia la selecciona.
Ejecutar una regla supone ejecutar las acciones de la RHS
vehículos de color verde. (defrule regla-ejemplo "Ejemplo de regla"
(frigorifico interruptor encendido)
Como contenido de una variable también
(frigorifico puerta abierta)
podemos incluir la dirección o identificativo de =>
un hecho. El símbolo <- indica la transferencia (assert (frigorifico comida estropeada)))
de la dirección del hecho que se indica a la
derecha sobre la variable de su izquierda:
(deftemplate modificacion-color

10
Apuntes de Ingeniería y Gestión del Conocimiento

(slot matricula)
(slot color))
(defrule realizar-modificacion-de-color)
?f1 <- (modificacion-color (matricula ?matricula) (color ?color))
?f2 <- (vehiculo (matricula ?matricula))
=> (retract ?f1)
(modify ?f2 (color ?color)))

Gracias a retract la regla no entra en un ciclo infinito. El ejemplo muestra la


regla que, al ocurrir un hecho como anotación
de un cambio (modificacion-color), modifica el Variables locales, locales a la regla o funcion
hecho que había registrado el color del vehículo ?variable permite el pattern-matching
$?variable, especial para multivalores
correspondiente.
Variables globales:
El comando bind permite cargar una variable Permiten almacenar valores accesibles en reglas
con el resultado de una expresión (solo en la y funciones, muy útiles para resultados
RHS): (defglobal
(bind ?area (*?base ?altura)) ?*num* = 3
?*suma* = (+ ?*num* 2)
Cuando un slot es multivaluado, ?*cadena* = “hola”
denominaremos cada posible valor como ?*lista* = (create$ a b c)
campo. Así, en la parte izquierda de una reglas )
Pueden aparecer en la parte izquierda de las
es posible referenciar a cada campo de forma reglas si no son utilizadas para asignar un valor y su
independiente, a través de la posición que cambio no activa las reglas, pero no pueden ser
ocupa, referenciando previamente todas las parámetros de funciones ni métodos
posiciones (campos) anteriores. Por ejemplo el
slot matricula podíamos haberlo definido como EC que carga el índice del hecho
multivaluado (multislot) de tal forma que se
?<var-direccion> <- (<elemento-condicional>)
pueda hacer referencia a cada uno de sus tres
componentes por diferenciado (letras, número, (defrule elimina-pacientes-fiebre
letras). La regla del siguiente ejemplo imprimir ?fiebre <- (paciente (nombre ?n) (temp-ahora
las matrículas de los vehículos de color verde, ?t &: (> ?t 37)))
=>
separando sus componentes con un guión. En la (printout t "El paciente: " ?n “ tiene fiebre” crlf)
parte izquierda de la regla, las variables ?letras- (retract ?fiebre)
ini, ?numero y ?letras-fin toman los valores del )
primer, segundo y tercer componente del
multislot matrícula.
(defrule imprime-matriculas-verdes
(vehiculo (color verde))
(matricula ?letras-ini ?numero ?letras-fin))
=> (printout t ?letras-ini "-" ?numero "-" ?letras-fin crlf))
Pero otras veces no hace falta nombrar las variables puesto que posteriormente
no se va a hacer uso de ellas, porque interesa únicamente un componente del

11
Tema 2. Desarrollo de SBCs en Clips

multislot. Por ejemplo si queremos imprimir únicamente las letras finales de la


matrícula de cada coche, cambiaríamos los nombres de variables por ? sin más:
(defrule imprime-matriculas-verdes
(vehiculo (color verde))
(matricula ? ? ?letras-fin))
=> (printout t ?letras-fin crlf))

Para los casos en que interesa comparar un valor con todos los campos de un
multislot, por ejemplo buscar las letras IX en la matrícula, que pueden aparecer en
el primer campo o en el último, se dispone del símbolo $?, que representa a cero o
más campos. En el ejemplo se imprimirían los nombres de los propietarios cuyos
vehículos tengan matriculas que contengan esos dos caracteres.
(defrule imprime-matriculas-IX
(vehiculo (matricula $? IX) Patrones en los ECs:
(propietario ?propietario))
=> (printout t ?propietario crlf)) Con restricciones literales.
(temperatura 28 grados)
El caracter $ tiene también la función de designar a
una variable como multivaluada. En el siguiente ejemplo Con comodines simples y multicampo.
se imprimen los nombres de los propietarios y las (temperatura ? grados)
(pacientes $? Lopez $?)
matrículas de sus vehículos. Sin este símbolo, la variable
?matricula solo haría referencia a su primer campo. Con variables simples y multicampo.
Obsérvese como en la parte derecha de la regla el (temperatura ?t grados)
carácter $ ya no precede al nombre de la variable (pacientes ?primero $?resto)parámetros de
multivaluada, porque ya capturó todo su contenido en la funciones ni métodos
Con operadores lógicos.
parte izquierda. (opcion-elegida 1|2|3)
(defrule imprime-propietario-matriculas (num-pacientes ~0)
(vehiculo (matricula $?matricula) (opcion-elegida ~1&~2&~3)
(propietario ?propietario))
=> (printout t ?propietario " es Con predicados o llamadas a funciones:
propietario de " ?matricula crlf)) (temperatura (maximo ?tmax)
(corporal ?t &:(> ?t ?tmax)))
Como ejemplo final de este apartado mostramos las
reglas que permiten manejar una pila. Se parte de un
multislot pila y de un slot nuevo-valor que contiene el ECs:
valor a insertar.
(defrule apilar Tipo test: (test <llamada-a-función>)
(temp-max ?tmax)
?apilar <- (nuevo-valor ?valor) (temp-corporal ?t)
?pila <- (pila ?ant-contenido) (test (>= ?tmax ?t)
=> (retract ?apilar ?pila)
(assert (pila ?valor ?ant-contenido)) Tipo or: (or <elemento-condicional>+)
(printout t "Apilado "?valor crlf))
(defrule desapilar-correcto (or (temperatura-corporal alta)
?desapilar <- (desapilar) (presion-arterial baja))
?pila <- (pila ?cima ?resto)
Tipo and: (and <elemento-condicional>+)
12
(or (and (presion-arterial baja) (temperatura-
corporal baja))
(and (presion ?p&:(<?p 1500) (temperatura
?t&:(< ?t 35)
Apuntes de Ingeniería y Gestión del Conocimiento

=> (retract ?desapilar ?pila)


(assert (pila ?resto))
(printout t "Desapilado el valor "?cima crlf))
(defrule desapilar-incorrecto
?desapilar <- (desapilar)
(pila)
=> (retract ?desapilar)
(printout t "Error, desapilando de pila vacia" crlf))
Las restricciones de la parte izquierda de una regla admiten una serie de
operadores lógicos como not, and y or. Por ejemplo, la siguiente regla imprime los
nombres de los propietarios de vehículos cuyo color no es verde.
(defrule imprime-propietarios-vehiculos-no-verdes
(vehiculo (color ~verde)
(propietario ?propietario))
=> (printout t ?propietario crlf))

La siguiente imprimiría los propietarios de vehículos de color verde o rojo:


(defrule imprime-propietarios-vehiculos-rojos-verdes
(vehiculo (color verde | rojo)
(propietario ?propietario))
=> (printout t ?propietario crlf))

El operador and se suele utilizar conjuntamente con otras restricciones. Por


ejemplo, si a la regla anterior deseamos incluirle la impresión del color de cada
vehículo seleccionado:
(defrule imprime-propietarios-vehiculos-rojos-verdes
(vehiculo (color ?color&verde | rojo)
(propietario ?propietario))
=> (printout t ?propietario "tiene un vehiculo de color " ?color crlf))

?color&verde | rojo es la expresión para ligar la variable al mismo tiempo que


restringe el valor que puede tomar.
La regla opuesta, es decir, que imprima para los vehículos de color diferente al
rojo y verde sería:
(defrule imprime-propietarios-vehiculos-rojos-verdes
(vehiculo (color ?color&~verde & ~rojo)
(propietario ?propietario))
=> (printout t ?propietario " tiene un vehiculo de color " ?color crlf))

Pero en la definición de patrones como restricciones no solamente pueden


intervenir constantes (verde, rojo) sino el valor booleano resultante de una
expresión, a través del carácter “:” antes de la expresión. En el siguiente ejemplo se
imprimen los propietarios de aquellos vehículos con más de 12 meses de
antigüedad.

13
Tema 2. Desarrollo de SBCs en Clips

(defrule imprime-propietarios-vehiculos-media-antiguedad
(vehiculo (antiguedad ?antiguedad&: (> ?antiguedad 12) &: (< ?antiguedad 24))

(propietario ?propietario))
=> (printout t ?propietario " tiene un vehiculo de uno a dos años de
antiguedad" crlf))
Claro que también se podría haber utilizado la función test, que devuelve el
valor booleano de una expresión:
(defrule imprime-propietarios-vehiculos-antiguos Acciones procedurales:
(vehiculo (antiguedad ?antiguedad) Asignación a una variable
(propietario ?propietario)) (bind <variable> <expresión>*) (sólo en la
(test (> ?antiguedad 12)) RHS)
=> (printout t ?propietario " tiene un vehiculo Condicional:
con más de 12 meses" crlf)) (if <expresión> then <acción>+ [else <acción>+])
(switch <expresión-test> <sentencia-
Vamos a ver un ejemplo de utilización de las case><sentencia-case>+
[(default <acción>*)])
restricciones, como la anteriores, pero para seleccionar <sentencia-case> ::= (case <comparación> then
dos instancias (hechos) de la misma clase y en la misma <acción>*)
regla, que imprime los nombres de dos propietarios de Iteraciones:
sendos vehículos del mismo color. (while <expresión> [do] <acción>*)
(defrule imprime-propietarios-vehiculos-mismo-color (loop-for-count (<var> <inicio> <final>) [do]
(vehiculo (propietario ?propietario1) (color ?color1)) <acción>*)
(break)
(vehiculo (propietario ?propietario2&~?propietario1)
(color ?color2&?color1))
=> (printout t ?propietario1 " y " ?propietario2 " tienen un vehículo del
mismo color " ?color1 crlf))

En este otro ejemplo, la expresión de la restricción se hace algo más compleja:


(defrule imprime-propietarios-vehiculos-requieren-ITV
(vehiculo (antiguedad ?antiguedad&:(= ?antiguedad 5) |:(> ?antiguedad 5)&:(=
(mod (- ?antiguedad 5) 2) 0))
(matricula ?matricula))
=> (printout t "El vehiculo " ?matricula " requiere ITV" crlf))
Una comparación de patrones pueden incluir la evaluación de funciones,
siempre y cuando, el resultado sea un único valor, que permita, al evaluar la
expresión, determinar si se cumple la premisa, o no, para una instancia o hecho
determinado, sin necesidad de cargar el valor sobre una variable. En el siguiente
ejemplo, ficticio por supuesto, el tipo de vehículo coincide con el múltiplo de 500
que es su cilindrada, y este tipo esta relacionado con el impuesto de circulación a
través de una tabla-impuestos:
(deftemplate impuesto-circulacion
(slot tipo)
(slot impuesto))

14
Apuntes de Ingeniería y Gestión del Conocimiento

(defrule imprime-vehiculos-impuestos-circulacion
(vehiculo (cilindrada ?cilindrada)
(matricula ?matricula))
(impuesto-circulacion (tipo =(div ?cilindrada
500))
(impuesto ?impuesto))
=> (printout t "El vehiculo " ?matricula " tiene
que pagar " ?impuesto crlf))
(deffacts tabla-impuestos
(impuesto-circulacion (tipo 3)(impuesto 150)) Funciones predefinidas
(impuesto-circulacion (tipo 4)(impuesto 225)) Funciones de entorno:
(impuesto-circulacion (tipo 5)(impuesto 250))) (load <nombre-de-fichero>)
(load-facts <nombre-de-fichero>)
Los anteriores ejemplos se han basado en (save <nombre-de-fichero>)
restricciones sobre slots o variables pero que no se (save-facts <nombre-de-fichero>)
(exit) ; cierra el entorno Clips
combinaban en una misma expresión de restricción.
Matemáticas: abs, div, mod, round..., sin, cos..
Pero esto también es posible mediante las funciones or, (<op> <expresión-numérica> <expresión-
and y not. Entre las premisas de una misma regla numérica>+)
evidentemente hay ya un and implícito, el uso explícito E/S: (open <nombre-fichero> <nombre-lógico>
de esta función tendrá sentido al combinarse con las [<modo>])
(close [<nombre-lógico>])
otras dos. En el caso del or hay que tener en cuenta en su (read [<nombre-lógico>])
utilización, que puede generar varias concordancias del (printout <nombre-lógico> <expresión>*)
patrón de las premisas, por ejemplo: Strings: (str-cat <expresión>*)
(defrule imprime-vehiculos-seguro-TipoB (sym-cat <expresión>*)
(or (str-index
(vehiculo (antiguedad ?antiguedad&:(> ?antiguedad 120))) <expresión-lexema> <expresión-
lexema>)
(vehiculo (kilometraje ?kilometraje&:(> ?kilometraje 150000))))
(sub-string <inicio> <fin> <expresión-de-cadena>)
(vehiculo (matricula ?matricula))
(str-length <expresión-simbólica-o-de-cadena>)
=> (printout t "El vehiculo " ?matricula " paga seguro tipo B " crlf))

Ante estos hechos:


(deffacts vehiculos-vendidos
(vehiculo (matricula MIB-3456-ERC)
(antiguedad 124)
(kilometraje 200000))
(vehiculo (matricula BBB-6765-IXC)
(antiguedad 11)
(kilometraje 100000)))

La respuesta del lanzamiento de la regla serían cuatro líneas en vez una, que
sería lo correcto. Para que funcionar adecuadamente la regla debería haber sido la
siguiente:
(defrule imprime-vehiculos-seguro-TipoB
(vehiculo (antiguedad ?antiguedad)(kilometraje ?kilometraje)
(matricula ?matricula))
(test (or (> ?antiguedad 120)(> ?kilometraje 150000)))

15
Tema 2. Desarrollo de SBCs en Clips

=> (printout t "El vehiculo " ?matricula " paga seguro tipo B " crlf))
En este otro ejemplo, la ejecución de la regla obtendrá la máxima antigüedad de
entre todos los vehículos:
(defrule imprime-mayor-antiguedad
(vehiculo (antiguedad ?antiguedad1))
(not (vehiculo (antiguedad ?antiguedad2&:(> ?antiguedad2 ?antiguedad1 ))))
=>(printout t "La máxima antiguedad de un vehiculo es " ?antiguedad1 crlf))
Ahora que, si queremos obtener la matrícula del coche de mayor antigüedad, la
regla quedaría así:
(defrule imprime-vehiculo-mayor-antiguedad
(vehiculo (antiguedad ?antiguedad1)(matricula ?matricula))
(not (vehiculo (antiguedad ?antiguedad2&:(> ?antiguedad2 ?antiguedad1 ))))
=>(printout t "El vehiculo de maxima antiguedad es " ?matricula crlf))

Sabemos que la siguiente regla:


(defrule imprime-si-hay-vehiculos Manipulación de valores multicampo:
(vehiculo)
(create$ <expresión>*)
=>(printout t "existe un vehiculo" crlf))
(delete$ <expresión-multicampo> <posición-inicio><posición-
imprimiría tantas líneas como vehículos final>)
(explode$ <expresión-string>)
tengamos afirmados, para comprobar si al (first$ <expresión-multicampo>)
menos hay un vehículo y no tener que listarlos (rest$ <expresión-multicampo>)
todos, podría utilizarse el operador exists, no (implode$ <expresión-multicampo>)
entra en el patrón un vehículo en particular, de (insert$ <expr-multicampo> <expr-entera><expresión-simple-o-
multicampo>+)
la siguiente forma: (length$ <expresión-multicampo>)
(defrule imprime-si-hay-vehiculos (member$ <expresión-campo-simple> <expresión-multicampo>)
(exists (vehiculo)) (nth$ <expresión-entera> <expresión-multicampo>)
=>(printout t "existe un vehiculo" crlf)) (replace$ <expresión-multicampo> <inicio> <final><expresión-
simple-o-multicampo>)
El operador de funcionalidad inversa es (subseq$ <expresión-multicampo> <inicio> <final>)
forall, en el siguiente ejemplo, la regla imprimiría(sym-cat
un mesaje unicamente si todos
<expresión>*)
(str-index
los vehículos que tienen más de 1500cc tienen también más<expresión-lexema>
de 100 caballos: <expresión-lexema>)
(defrule comprueba-cilindrada-CV (sub-string <inicio> <fin> <expresión-de-cadena>)
(str-length <expresión-simbólica-o-de-cadena>)
(forall (vehiculo (cilindrada ?cilindrada&:(> ?cilindrada 1500))(matricula
?matricula))
(vehiculo(CV ?cv&:(> ?cv 100))(matricula ?matricula)))
=>
(printout t "Todo vehiculo con mas de 1500 cc tiene mas de 100 cv" crlf))

En este punto es aconsejable estudiar el ejemplo sticks.clp (curso virtual). Es


muy importante estudiar detenidamente la utilización de los identificativos de
hechos y de la función retract para el control de la ejecución de las reglas.

16
Apuntes de Ingeniería y Gestión del Conocimiento

4ENTRADA/SALIDA DE DATOS
Hemos estado utilizando la función printout (t es el nombre de referencia del
terminal estándar, normalmente pantalla para salida y teclado para entrada) para
visualizar datos por el terminal. La función read puede utilizarse para el proceso
complementario de captura de datos desde el teclado. Devuelve el valor y el control
al introducirse el enter:
(defrule introducir-propietario
=> (printout t "Nombre del propietario: ")
(bind ?propietario (read))
(assert nombre-propietario ?propietario))
La función bind almacena en la variable, que se indica como primer argumento
en el comando, el valor resultante de la expresión del segundo argumento.
Las funciones para lectura escritura en ficheros son también estas mismas read
y printout, pero se requiere que los correspondientes ficheros hayan sido
previamente abiertos con (open nom-archivo identificador tipo-acceso). Por
ejemplo: (open “camiones.dat” camiones “r”) abrirá el archivo camiones.dat para
lectura y le asignará el identificador interno camiones. Los tipos pueden ser “r”
para solo lectura, “w” para solo escritura, “r+” lectura y escritura y “a” para poder
únicamente añadir.
Existe la posibilidad de en vez de utilizar read para leer desde un fichero de
texto, utilizar (readline identificador) para leer caracteres hasta el cambio de línea.
Esta línea se introduce como un campo simple, si se desea descomponer, se puede
utilizar la función explode$, veamos el siguiente ejemplo:
(defrule obtener-propietario
=> (printout t "Nombre del propietario: ")
(bind ?propietario (explode$(readline)))
(assert (nombre-propietario ?propietario))
Si a la pregunta de esta regla respondemos con tres palabras (nombre y
apellidos), la regla producirá 3 nuevos hechos. Si no hubiéramos utilizado
explode$, se hubiese producido un único hecho.

17
Tema 2. Desarrollo de SBCs en Clips

Similar a la función printout, la función format permite trasladar a la salida los


datos especificados en un formato determinado, habitualmente con el objetivo de
estructurar estos datos en tablas (ver documentación).

Funciones de entrada y salida:


(open <nombre-fichero> <nombre-lógico> [<modo>])
Abre un fichero con el modo especificado (“r”, “w”, “r+”, “a”) asociando el
nombre
de fichero dado al nombre lógico especificado. Si no se especifica modo, se
supone “r”
(close [<nombre-lógico>])
Cierra el fichero asociado con el nombre lógico especificado. Si no se
especifica el
nombre lógico, se cierran todos los ficheros.
(printout <nombre-lógico> <expresión>*)
Evalúa e imprime como salida al nombre lógico 0 o más expresiones no
formateadas.
(read [<nombre-lógico>])
Lee un campo simple (single field) desde el nombre lógico especificado. Si
no se especifica nombre lógico, se supondrá stdin (entrada estándar). Devuelve
el elemento leído (que será siempre de un tipo primitivo) si tiene éxito, o EOF
si no hay entrada disponible. Ej.: (assert (habitación (número (read))))
(readline [<nombre-lógico>])
Lee una línea completa desde el nombre lógico especificado (supondrá stdin,
si no se especifica ninguno). Devuelve un string si tuvo éxito, o EOF si no
había entrada disponible.
(format <nombre-lógico> <string-control> <parámetros>*)
Envía al dispositivo asociado con el nombre lógico especificado un string
formateado. El string de control contiene unos flags de formato que indican
cómo serán impresos los parámetros. El funcionamiento es similar a la función
printf del lenguaje C. La función devuelve el string impreso.

5 MANTENIMIENTO DE LA VERDAD
En Clips, cuando una regla da lugar a un nuevo hecho, éste no desaparece
cuando, ante nuevos eventos, las condiciones de las premisas de la regla ya no se
cumplen. Para algunas situaciones o problemas esto está bien así, es coherente con
el conocimiento que se desea representar, pero en otras ocasiones no. Ante el
incumplimiento de una premisa de una regla, deberían desaparecer los hechos a los
que previamente dio lugar. Para Clips dispone del operador logical, que crea

18
Apuntes de Ingeniería y Gestión del Conocimiento

dependencias entre los hechos que coinciden con los patrones contenidos por el
operador, en el lado izquierdo de la regla, y los hechos afirmados en la parte
derecha.
Dado el siguiente hecho (sea f-4) y al lanzar la siguiente regla, se afirmaría un
nuevo hecho (sea f-5):
(deffacts incautaciones
(incautado (matricula BBB-6765-IXC)))
(defrule determina-incautados
(logical (incautado (matricula ?matricula)))
(vehiculo (matricula ?matricula)(inhabilitado false))
=> (assert (vehiculo (matricula ?matricula)
(inhabilitado true))))
De tal forma que si retiramos este hecho, con (retract 4), desaparecen de la base
de hechos tanto f-4 como f-5.

6 PRIORIDAD Y CONTROL
Por defecto, el orden de ejecución de las reglas depende de su orden de llegada
a la pila de la agenda, por tanto y como tal pila, se activan primero las últimas
reglas recibidas. El orden de llegada a la agenda depende a su vez del orden en la
afirmación de los hechos que las disparan. Pero este orden de ejecución puede ser
alterado gracias a la posibilidad de establecer prioridades para cada regla particular.
Es posible definir un nivel de prioridad desde –10.000 a +10.000, siendo el valor
por defecto 0. Se indica con (declare(salience n)) como premisa de la parte
izquierda de la regla. En definitiva, en la agenda las reglas se ordenan por su valor
salience y, con igual valor, por su orden inverso de llegada.

19
Tema 2. Desarrollo de SBCs en Clips

Motor de inferencia: Ciclo Básico:


1. Si se ha alcanzado el número de ciclo de desencadenamiento expresados o
no hay foco actual, entonces PARAR.
Si no, seleccionar para ejecutar (2) la regla tope de la agenda del módulo
actual.
Si no hay reglas en dicha agenda, entonces eliminar el foco actual de la
pila de focos y actualizar al siguiente de la pila.
Si la pila de focos está vacía, entonces PARAR, si no ejecutar 1. de nuevo.
2. Se ejecutan las acciones de la parte derecha de la regla seleccionada.
Incrementar el nº de reglas desencadenadas.
Si se usa return, entonces eliminar el foco actual de la pila de focos.
Como resultado acciones se activan o desactivan nuevas reglas.
Las reglas activadas se sitúan en la agenda del módulo en el que están
definidas.
La situación concreta depende de la prioridad de la regla (salience) y
de la estrategia de resolución de conflictos actual. Las reglas desactivadas
salen de la agenda.
Si se utiliza prioridad dinámica (dynamic salience), entonces se
reevalúan los valores de prioridad de las reglas de la agenda.
Ir al paso 1.
declare (salience -100)
Permite al usuario asignar una prioridad a una regla, entre -10000 y
+10000, por defecto 0.

En la mayoría de los sistemas expertos que se desarrollen, en cuanto tienen


una cierta complejidad, se pueden identificar fases o secuencias de aplicación de
reglas, esto es, admiten una cierta visión procedimental: Reglas que solucionan el
subproblema-1, las que solucionan el subproblema-2, etc. y hay que secuenciarlas
adecuadamente. Las reglas desde luego se activan cada vez que se cumplen sus
premisas, que son aplicables, esto es lo esencial, pero también, como decimos, hay
que establecer una cierta secuenciación o flujo de control. Por ejemplo, en las Fig.
1 y Fig. 2 se describe la secuencia de control en el lanzamiento las reglas en las
que se apoyan los pasos de inferencia recubre, predice y compara en el que se ha
descompuesto la tarea de diagnóstico.

20
Apuntes de Ingeniería y Gestión del Conocimiento

alarma el motor no arranca

indicador de gasolina = normal

reglas de dependencia de estados


hallazgo
recubre modelo causal obtiene observado

hipótesis predice hallazgo compara


esperado
tanque de gasolina
vacío indicador de gasolina = cero/bajo
resultado
modelo de efectos

distinto

Fig. 1 Ejemplo de esquema inferencial que supone una secuenciación en la ejecución


de los distintos tipos de reglas

TASK-METHOD diagnóstico-a- través-sugiere-y-comprueba;


REALIZES: diagnóstico-vehículo;
DECOMPOSITION:
INFERENCES: recubre, predice, compara;
TRANSFER-FUNCTIONS: obtiene;
ROLES:
INTERMEDIATE:
hipótesis: “una posible solución”;
hallazgo-esperado: “un hallazgo que se debería observar si la hipótesis fuera cierta”;
hallazgo-observado : “el hallazgo realmente observado”;
resultado : “el resultado de la comparación”;
CONTROL-STRUCTURE:
WHILE NEW-SOLUTION recubre(aviso->hipótesis) DO
predice(hipótesis->hallazgo-esperado);
obtiene(hallazgo-esperado-> hallazgo-observado),
evidencia:=evidencia ADD hallazgo-observado);
compara(hallazgo-observado+ hallazgo-esperado -> resultado);
IF resultado == igual THEN BREAK-LOOP END IF;
ENDWHILE;
IF resultado == igual THEN tipo-fallo:= hipótesis
ELSE “no se ha encontrado solución”
END IF;
END TASK-METHOD diagnóstico-a- través-sugiere-y-comprueba;

Fig. 2 Ejemplo en CML en el que se describe el método utilizado para la resolución de


la tarea, especificando las subtareas y la estructura de control sobre ellas.

21
Tema 2. Desarrollo de SBCs en Clips

Para establecer una estructura de control de este tipo mediante reglas como las
que hemos visto, podemos utilizar uno de los siguientes métodos. En los ejemplos
se supondrá que la subtareas se suceden sin interrupción.
Incluir en cada regla una premisa que determine la fase (predice, obtiene, etc.)
del proceso en que podrá ser activada y la afirmación del hecho que sitúa al sistema
en la siguiente fase (si ha lugar):
(defrule predice-...
?fase <-(fase predice)
...
=> ...
(retract ?fase)
(assert (fase obtiene)))

Otra opción es secuenciar a través de los niveles de prioridad, pero esto tiene el
problema de que se pueden mezclar las fases, en tanto que, si en la fase obtiene
entra a la agenda una regla de las de apoyo al predice, esta se activará antes que
restantes correspondientes a obtiene. El ejemplo auto.clp (en el sitio web de Clips)
utiliza este sistema.
La tercera opción, podríamos decir que híbrida de las anteriores, es definir unas
reglas específicamente para secuenciar las fases, que tienen una prioridad más baja
que las reglas que soportan el conocimiento experto propiamente dicho, y que
cambian el hecho de define las subtarea activa.
(defrule predecir-obtener
(declare (salience –100))
?subtarea <- (subtarea predecir)
=> (retract ?subtarea)
(assert (subtarea obtener)))
(defrule obtener-comparar
(declare (salience –100))
?subtarea <- (subtarea obtener)
=> (retract ?subtarea)
(assert (subtarea comparar)))
(defrule comparar-predecir
(declare (salience –100))
?subtarea <- (subtarea comparar)
=> (retract ?subtarea)
(assert (subtarea predecir)))
Cada regla “experta” comienza con una premisa que la identifica como
perteneciente al conocimiento de apoyo de una subtarea:
(defrule predice-...
(subtarea predecir)
; premisas que determinan la hipótesis de una avería

22
Apuntes de Ingeniería y Gestión del Conocimiento

...
=> ...
)
Alternativamente a esta opción, podría escribirse una regla única de control que
fuera lanzando las sucesivas subtareas sin tener que aparecer explícitamente en la
regla, de esta forma:
(defrule lanza-subtarea
(declare (salience –100))
?subtarea <- (subtarea ?subtarea-actual)
(subtarea-siguiente-a ?subtarea-actual ?subtarea-siguiente)
=> (retract ?subtarea)
(assert (subtarea ?subtarea-siguiente)))
Siempre y cuando se hayan definido estas fases como hechos sucesivos de un
slot fase-posterior:
(deffacts secuencia-subtareas
(subtarea predecir)
(subtarea-siguiente-a predecir obtener)
(subtarea-siguiente-a obtener comparar)
(subtarea-siguiente-a comparar predecir))

Algo totalmente equivalente sería utilizar una lista de subtareas y una regla que
las secuenciara, es decir:
(defrule lanza-subtarea
(declare (salience –100))
?subt <- (subtarea ?subt-actual)
?sec-subts <- (secuencia-subts ?subt-siguiente $?otras-subts)
=> (retract ?subt ?sec-subts)
(assert (subtarea ?subt-siguiente))
(assert (secuencia-subts ?otras-subts ?subt-siguiente)))
Con
(deffacts estructura-subtareas
(subtarea predecir)
(secuencia-subts obtener comparar predecir))
Según cualquiera de estas opciones anteriores, tendríamos dos niveles de
prioridad, uno asignado a las reglas que hemos denominado expertas y otro a las
reglas de control. Aún podrían introducirse dos niveles más, también para el
correcto control en la evaluación de las reglas. El mayor nivel de prioridad
deberían tenerlo aquellas reglas que detectan situaciones que se han definido como
ilegales del programa. Las reglas especificas para plantear preguntas al usuario
deber deberían tener un nivel intermedio de prioridad, entre las reglas de control y
las reglas expertas, de tal forma que solo se pregunte al usuario cuando no haya
conocimiento experto para inferir el valor que se cuestiona.

23
Tema 2. Desarrollo de SBCs en Clips

Pero no deberían utilizarse muchos más niveles de prioridad, desde luego no


dentro de las reglas “expertas”, estas deben contener en sus premisas la
determinación exacta y explícita de la situación que las hacen activarse. Así, en un
sistema algo grande, es preferible utilizar módulos contenedores compactos de
reglas, y por tanto más sistemas de reglas en sí más reducidos, que se activan según
una estructura de control procedimental.

Instancias de una regla. Agenda

Se construye una instanciación de la regla para cada conjunto de elementos


que satisfacen la LHS.
<prioridad> <nombre-regla> : fact-index*
fact-index := f-<número-entero>
Fases:
1: Se toman todos los elementos que satisfacen cada una de sus
condiciones.
2: Se toman las combinaciones de elementos que satisfacen la restricción
impuesta por las variables que relacionan condiciones (aparecen los mismos
nombres de variables.

La agenda es la lista de todas estas las reglas cuyas condiciones se


satisfacen y todavía no se han ejecutado.

Resolución de conflictos en la Agenda

Cuando una regla se activa de nuevo, se sitúa en la agenda...:


a) por encima de todas las reglas de menor prioridad y por debajo de todas las
reglas de mayor prioridad y.
b) entre las reglas de igual prioridad, se utiliza la estrategia de resolución de
conflictos actual para determinar su situación relativa.
c) Si (a) y (b) no son suficientes para determinar el orden, las reglas se
colocan arbitrariamente en relación con las otras reglas. No aleatoriamente,
sino que depende de la implementación de las reglas.
Estrategias: hay 7, las principales
depth (estrategia por defecto): Las nuevas reglas se sitúan sobre
todas las de la misma prioridad.
breadth: Las nuevas reglas se sitúan por debajo de todas las de la
misma prioridad.
random: A cada activación se le asigna un número aleatorio que se
24
utilizará para determinar su lugar entre las activaciones de igual prioridad
(este número se mantiene entre cambios de estrategia).
(set-strategy <nombre-estrategia>)
Apuntes de Ingeniería y Gestión del Conocimiento

Eficiencia de las reglas:

Los patrones más específicos, con más restricciones, deben de ir primero, ya que,
generalmente, producirán el número más pequeño de coincidencias y tendrán un
mayor número de variables asignadas. Lo que provocará una mayor restricción en
los otros patrones.
➮ Los patrones temporales deben de ir los ú́ ltimos. Es decir, los
correspondientes a hechos que se añaden y borran frecuentemente de la lista de
hechos. Esto provocará un menor número de cambios en las coincidencias
parciales.
➮ Los patrones más raros deben ser los primeros. Los patrones
correspondientes a hechos que se presentarán pocas veces en la lista de hechos, si se
colocan al principio reducirán el número de coincidencias parciales.
➮ La función test debe ponerse lo antes posible: ¿Así, cuál es mejor de R1 y
R2?
(defrule R1
?p1 <- (valor ?x)
?p2 <- (valor ?y)
?p3 <- (valor ?z) ; comprueba que ningun valor es igual a otro
(test (and (neq ?p1 ?p2) (neq ?p2 ?p3) (neq ?p1 ?p3))
=> )
(defrule R2
?p1 <- (valor ?x)
?p2 <- (valor ?y)
(test (neq ?p1 ?p2))
?p3 <- (valor ?z)
(test (and (neq ?p2 ?p3) (neq ?p1 ?p3))
=> )

Procesos sin fin: El mismo caso


(deffacts myfact (defrule sexo_usuario
(loop-fact)) ?f2 <- (vehiculo (sexo_usuario ?sexo))
=> (printout t crlf "Sexo del usario: " crlf)
(defrule simple-loop (bind ?sexo (read))
?old-fact <- (loop-fact) (modify ?f2 (sexo_usuario ?sexo)))
=>
(printout t “Looping!” crlf)
(retract ?oldfact)
(assert (loop-fact)))

; una variante
(deftemplate registro 25
(slot anda)
(slot ya))
(deffacts error
(registro (anda nil) (ya nil))

(defrule anda-que
?anda <- (registro (anda ?var))
=>
(modify ?anda (anda ?var) (ya bien)))
Tema 2. Desarrollo de SBCs en Clips

Del control de las reglas y su tipología:


En principio tenemos dos niveles de prioridad, las reglas que hemos denominado
expertas y otro a las reglas de control.
Podrían introducirse dos niveles más, también para el correcto control en la
evaluación de las reglas.
El mayor nivel de prioridad deberían tenerlo aquellas reglas que detectan
situaciones que se han definido como ilegales del programa.
Las reglas especificas para plantear preguntas al usuario deber deberían
tener un nivel intermedio de prioridad, entre las reglas de control y las reglas
expertas, de tal forma que solo se pregunte al usuario cuando no haya conocimiento
experto para inferir el valor que se cuestiona.
Pero no deberían utilizarse muchos más niveles de prioridad, desde luego no
dentro de las reglas “expertas”, estas deben contener en sus premisas la
determinación exacta y explícita de la situación que las hacen activarse.
Así, en un sistema algo grande, es preferible utilizar módulos contenedores
compactos de reglas, y por tanto más sistemas de reglas en sí más reducidos, que se
activan según una estructura de control procedimental.

7. PROGRAMACIÓN PROCEDIMENTAL
Clips dispone de algunas funciones para implementar las estructuras de control
propias de la programación procedimental. En conjunción con las reglas, se pueden
utilizar estas estructuras principalmente expresadas en la parte derecha de la regla,
de tal forma, que la veracidad de las premisas suponga la ejecución de un pequeño
procedimiento expresado, como decimos en la parte derecha. Principalmente son if,
while y halt. La primera obviamente soporta la estructura if..the..else, por todos
conocida. La segunda soporta la ejecución del típico bucle while y la función halt,
sin argumentos, permite parar la ejecución de las reglas de la agenda. La siguiente
regla nos muestra la utilización de estas tres funciones. En la secuencialidad de las
subtareas podemos dar la opción al usuario de parar momentáneamente o
definitivamente la ejecución.
(defrule plantear-revision
?subtarea <- (subtarea plantear-revision)
=> (retract ?subtarea)
(printout t "¿continuamos?(S/N) ")
(bind ?respuesta (read))
(while (and (neq ?respuesta S) (neq ?respuesta N)) do
(printout t "¿continuamos?(S/N) ")
(bind ?respuesta (read)))
(assert (subtarea continuar))
(if (neq ?respuesta S)

26
Apuntes de Ingeniería y Gestión del Conocimiento

then (halt)))
La función while nos permite seguir planteando la pregunta al usuario hasta que
éste responda una de los dos caracteres reconocidos. La función if permite parar la
ejecución, con halt, si la respuesta es afirmativa.

27
Tema 2. Desarrollo de SBCs en Clips

Funciones definidas por el usuario


(deffunction <nombre> [<comentario>] (<parámetro>* [<parámetro-comodín>])

<acción>*
)

El valor devuelto por la función es la última acción o expresión evaluada dentro de la función. Si una
deffunction no tiene acciones, devolverá el símbolo FALSE. Si se produce algún error mientras se ejecuta la
función, cualquier otra acción de la función aún no ejecutada se abortará, y la función devolverá el símbolo
FALSE.
Ejemplo: (subseq$ <expresión-multicampo> <inicio> <final>)
(sym-cat <expresión>*)
(str-index <expresión-lexema> <expresión-lexema>)
(sub-string <inicio> <fin> <expresión-de-cadena>)
(str-length <expresión-simbólica-o-de-cadena>)
(deffunction respuesta_si_no (?pregunta)
(bind ?respuesta (preguntar ?pregunta si no s n))
(if (or (eq ?respuesta si)
(eq ?respuesta s)) (deffunction preguntar (?pregunta $?valores-permitidos)
then TRUE (printout t crlf ?pregunta)
else FALSE)) (bind ?respuesta (read))
(if (lexemep ?respuesta) then
(bind ?respuesta (lowcase ?respuesta)))
(while (not (member ?respuesta ?valores-permitidos)) do
(printout t ?pregunta)
(bind ?respuesta (read))
(if (lexemep ?respuesta)
then (bind ?respuesta (lowcase ?respuesta))))
?respuesta)
Otro ejemplo:
(bind ?carroceria 0)
(while (or (< ?carroceria 1)(> ?carroceria 5))
(printout t crlf "Estado de la carroceria:")
(printout t crlf "1 - Muy bueno")
(printout t crlf "2 - Bueno")
(printout t crlf "3 - Regular")
(printout t crlf "4 - Malo")
(printout t crlf "5 - Muy malo")
(printout t crlf ": ")
(bind ?carroceria (read))
)
con:
(defrule pide-datos-iniciales "Pide los datos de matricula y anio de matriculacion"
(initial-fact)
=>
(printout t crlf "Datos del vehiculo")
(printout t crlf "Matricula: ")
(bind ?matricula (read))
(printout t crlf "Anio de matriculacion: ")
(bind ?anio (read))
(assert (vehiculo (matricula ?matricula)(anio-matriculacion ?anio)))
)
28
Apuntes de Ingeniería y Gestión del Conocimiento

Estructura Switch
(defrule precio_mecanico ""
?f2 <- (estado mecanico ?mec)
=>
(retract ?f2)
(if (or (eq ?mec leves) (eq ?mec graves)) then
(bind ?respuesta (preguntar_valor "Cuál es el precio de las reparaciones
mecánicas suministrado por los mecánicos (Pesetas): " )))
(switch ?mec
(case ninguna then (assert (estado mecanico ?mec 0)))
(case leves then (assert (estado mecanico ?mec ?respuesta)))
(case graves then (assert (estado mecanico ?mec ?respuesta)))
(case gravisimas then (assert (tasacion nula "Las reparaciones son
demasiado costosas")))))

Ejemplo de funciones:
(deffunction CalculaPorcentajeEntero(?Num ?Porcentaje)
(bind ?ParteEntera (div (* ?Num ?Porcentaje) 100))
(bind ?Resto (mod (* ?Num ?Porcentaje) 100))
(if (> ?Resto 0.5) then
(bind ?Resultado (+ ?ParteEntera 1))
else
(bind ?Resultado ?ParteEntera)
)
)

29
Tema 2. Desarrollo de SBCs en Clips

ejemplo de captura:
(deffunction es-del-tipo (?respuesta ?tipo ?posibilidades)
;; Si es de tipo mutiple recorre las posibilidades y devuelve TRUE si alguna
coincide.
;;;En caso contrario devuelve FALSE.
(if (eq ?tipo multiple) then
(progn$ (?item ?posibilidades) ; para cada elemento de la lista ?posibilidades
(if (eq ?respuesta ?item) then ;si coincide con la respuesta
(return TRUE))) ; devuelve TRUE
(return FALSE))
;; Si es de tipo numerico devuelve TRUE si la respuesta lo es y FALSE en
;; caso contrario.
(if (eq ?tipo numerico) then
(return (numberp ?respuesta)))
;; Para los demas casos devuelve TRUE si la respuesta tiene una longitud
;; mayor que 0 y FALSE en caso contrario.
(return (> (str-length ?respuesta) 0)))

Ejemplo de llamada desde una regla:


(deffunction factorial (?X)
(bind ?temp 1)
(loop-for-count (?i 1 ?X) do
(bind ?temp (* ?temp ?i))
)
(return ?temp)
)
(defrule ejemplo
=>
(printout t crlf crlf "Calcular factorial de: ")
(bind ?N (read))
(printout t "El resultado es " (factorial ?N) crlf)
)
El mismo proceso en una regla:
El mismo proceso en una regla:
(deffacts numbers
(defrule factorial
(number 56) (number -32)
=>
(number 7) (number 96)
(printout t crlf crlf "Enter a number: ") 30
(number 24))
(bind ?N (read))
(defrule find-largest-number
(bind ?temp 1)
(number ?x)
(loop-for-count (?i 1 ?N) do
(not (number ?y &: (> ?y ?x)))
(bind ?temp (* ?temp ?i))
=>
)
(printout t “Largest number is “ ?x crlf))
(printout t "Factorial=" ?temp crlf)
)
Apuntes de Ingeniería y Gestión del Conocimiento

(defrule sum-areas
Un bucle menos implícito
(declare (salience 20)) ;;; regla que
(deffacts initial-information ;datos de los
suma todas las areas
rectangulos de cuyas areas se obtendra la media
?sum-adr <- (sum ?total)
(rectangle 10 6)
?new-area-adr <- (add-to-sum ?area)
(rectangle 7 5)
?count-adr <- (count ?counter)
(rectangle 6 8)
=>
(rectangle 2 5)
(retract ?sum-adr ?new-area-adr
(rectangle 9 4)
?count-adr)
(sum 0)
(assert (sum (+ ?total ?area)))
(count 0))
(assert (count (+ ?counter 1))))
(defrule sum-rectangles
(defrule average
(declare (salience 30))
(declare (salience 10)) ;;; regla que
(rectangle ?height ?width)
calcula e imprime la media
=>
?sum-adr <- (sum ?total)
(assert (add-to-sum (* ?height
?count-adr <-(count ?counter)
?width)))) ; crea un hecho add-to-sum por cada
=>
rectangulo
(printout t crlf "Here is the average
area" (/ ?total ?counter) crlf))

Ejercicios con lo que sabemos hasta ahora:


Ejercicio-24: Permutaciones de los colores.
Ejercicio-20: Pasar a código binario. Tratando con strings.
Ejercicio-3: Dada una fecha, evaluar su corrección.
Ejercicio-1: Poner notas literales desde dos exámenes y las faltas de asistencia. Diferencia con la
versión procedimental. Diferentes formas de resolver el problema
Ejercicio-2: Asignar habitaciones
Ejercicio-15 y 16: Correo postal y viajes. Consulta de tablas.
Ejercicio-13: Diagnóstico de un motor (recorrido de un árbol binario)
Ejercicio-12: Fórmulas lógicas. Creando un árbol.
Ejercicio-5: Diagnóstico médico. Poniendo orden.
Ejercicio-10 : El mundo de bloques. También en: http://www.uco.es/~i82coory/bloques.htm
Ejercicio-19: Un problema de búsqueda. Problema de los cántaros. También solucionado en
http://www.uco.es/~i82coory/jarras.htm.

31
Tema 2. Desarrollo de SBCs en Clips

8 EJEMPLO DE TRATAMIENTO DE LA INCERTIDUMBRE EN


CLIPS: FACTORES MYCIN
Clips no incluye ningún mecanismo para el tratamiento de la incertidumbre, por
tanto, si queremos emplear alguna técnica para ello, hay que codificar el soporte y
los mecanismos asociados. Como ejemplo vamos a solucionar el tratamiento de la
incertidumbre con factores de certeza Mycin.
La representación en Mycin se apoya en el habitual par slot-valor, o dicho de
otra forma, en la terna objeto-atributo-valor, más un valor entre –1 y +1 que indica
el nivel de certeza o de evidencia de que el valor del slot sea el especificado, es
decir del hecho. Vamos a definir un template que soporte los cuatro valores:
(deftemplate oav-fc
(multislot objeto (type SYMBOL))
(multislot atributo (type SYMBOL))
(multislot valor)
(slot fc (type FLOAT) (range –1.0 +1.0)))
Por ejemplo:
(oav-fc (objeto microorganismo)
(atributo morfologia)
(valor bacilo)
(fc 0.7))

Una característica fundamental en Mycin es que pueden llegarse a los mismos


hechos desde diferente reglas o inferencias y esto afectará a su certeza, de tal forma
que es preciso combinar los diferentes factores de certeza obtenidos. Como es
posible que se obtengan valores idénticos para los factores desde, como decimos,
diferentes reglas, tenemos que decirle a Clips que admita hechos duplicados con la
instrucción:
(set-fact-duplication true)
Evidentemente, la misma instrucción con FALSE desactiva la posibilidad de
hechos duplicados.
Para combinar los hechos duplicados se precisa una regla que combine los
factores de certeza de dos hechos y que genere un nuevo hecho con el nuevo factor
combinado, anulando los hechos previos. Con la siguiente regla se consigue esto
para hechos con una factor de certeza positivo o cero.
(defrule combina-oav-fc-positivas
(declare (auto-focus true))
?hecho1 <- (oav-fc (objeto $?obj)
(atributo $?atri)
(valor $?val)

32
Apuntes de Ingeniería y Gestión del Conocimiento

(FC $?fc1&: (>= ?fc1 0)))


?hecho2 <- (oav-fc (objeto $?obj)
(atributo $?atri)
(valor $?val)
(FC $?fc2&: (>= ?fc2 0)))
(test (neq hecho1 hecho2))
=>
(retract ?hecho1)
(bind ?fc3 (- (+?fc1 ?fc2) (* ?Fc1 ?fc2)))
(modify ?hecho2 (FC ?fc3)))

La instrucción (declare (auto-focus true)) impide que se disparen antes otras


reglas, asegura que sea ésta la primera que se dispare al cumplirse las premisas. La
premisa con test garantiza que la regla se aplica sobre diferentes hechos. Como
podemos observar, la regla realmente no genera un tercer hecho y suprime los
anteriores, sino que suprime uno y modifica el otro.
Por otro lado, las reglas de la base de conocimientos tienen que tener en cuenta
un mecanismo de propagación de la certeza de la premisa sobre la certeza de la
conclusión. Sirva la siguiente regla Clips como ejemplo de estructura de regla
mycin que contiene el mecanismo de propagación de la evidencia. En este caso,
solamente se afirmará el hecho conclusión, es decir, se ha limitado la posibilidad
de transferir evidencia a la conclusión, cuando existan unos determinados hechos y
con factores superiores a 0.2:
(defrule combina-oav-FC-positivas
(oav-fc (objeto microorganismo)
(atributo coloracion)
(valor gramnegativo)
(fc ?fc1))
(oav-fc (objeto microorganismo)
(atributo morfologia)
(valor bacilo)
(fc ?fc2))
(oav-fc (objeto paciente)
(atributo es-un)
(valor huesped-afectado)
(fc ?fc3))
(test (> (min (?fc1 ?fc2 ?fc3) 0.2))
=>
(bind ?fc4 (* (min (?fc1 ?fc2 ?fc3) 0.6))
(assert (oav-fc (objeto microorganismo)
(atributo identidad)
(valor seudomonas)
(fc ?fc4))))

33
Tema 2. Desarrollo de SBCs en Clips

Como vemos, el factor de certeza de nuevo hecho generado en la conclusión es


el resultado de multiplicar el valor de certeza de la propia afirmación de la regla
(0.6) por el valor de certeza combinado de la premisa, el mínimo de los valores de
certeza de los hechos que intervienen en las premisas enlazadas por un y-lógico.

9 ÁRBOLES DE DECISIÓN.
Recordemos el problema de diagnóstico de una hemorragia a través de la
siguiente estructura:
“test de Rumpell Leede
positivo”

Cierto
Falso

“Recuento de
”VASCULITIS” plaquetas bajo”
Cierto
Falso

“No hay reducción o


ausencia de megacariocito” “Tiempo de tromboplastina
parcial activado normal”

Cierto Falso
Cierto Falso

“Tamaño normal del “APLASIA DE MÉDULA “Tiempo de


bazo” ÓSEA ” “Deficiencia del romboplastina parcial
FACTOR VII” activado bajo”
Cierto
Falso
Cierto Falso

“SECUESTRO “CAUSAS
“Deficiencia del “Deficiencia del
LEUCOCITARIO” INMUNOLOGICAS”
FACTOR XII, XI, FACTOR X, V,
IX o VIII” Prothrombina o
Fibrinógeno”

Fig. 3 Árbol de decisión binario

Es la estructura de lo que se denomina árbol de decisión binario, esto es, un


árbol compuesto por nodos intermedios, que representan afirmaciones (“El tiempo
de tromboplastina parcial activado es normal”), de los que salen enlaces, según la
afirmación sea cierta o no, hacia dos nodos descendientes. Los nodos hoja
representan afirmaciones finales que ya no se evalúan (“Secuestro leucocitario”).
También pueden verse los nodos intermedios como preguntas que solo tienen dos

34
Apuntes de Ingeniería y Gestión del Conocimiento

posibles respuestas Si y No (“¿Es normal el tiempo de tromboplastina parcial


activado?”) y los nodos hoja como afirmaciones concluyentes.
El algoritmo de recorrido de un árbol de decisión de este tipo es muy sencillo,
Comienzo
nodo-actual := nodo-raiz;
Mientras not(nodo-hoja(nodo-actual)) hacer
respuesta:= preguntar(nodo-actual);
Si respuesta =cierto entonces
nodo-actual := descendiente-cierto(nodo-actual);
Si respuesta =falso entonces
nodo-actual := descendiente-falso(nodo-actual);
Fin
Visualizar-afirmacion(nodo-actual)
Fin
En el seudocódigo empleado se ha supuesto una función preguntar(nodo-actual)
que pregunta al usuario si es cierta o falsa la afirmación contenida en el nodo-
actual y devuelve la respuesta. También sendas funciones que proporcionan los dos
descendientes del nodo-actual. Por último la función visualizar-afirmacion muestra
en la interfaz de salida la afirmación del nodo hoja en que queda la variable nodo-
actual tras el recorrido del árbol. Por supuesto esta función sería sustituible por
cualquier otro proceso sobre el nodo hoja localizado, por ejemplo, la simple
devolución como parámetro de vuelta del procedimiento.
Veamos ahora el código necesario en Clips para llevar a cabo lo expresado en
este seudocódigo. Podemos comenzar por definir las templates que soporten nodos
con esta estructura:

35
Tema 2. Desarrollo de SBCs en Clips

TNB
Afirmación: “Tamaño
normal del bazo”
Tipo: Intermedio
Siguiente-H-Cierto: SL
Siguiente-H-Falso: CI

SL CI
Afirmación: “Secuestro Afirmación: “Causas
Leucocitario” Inmunes”
Tipo: Hoja Tipo: Hoja
Siguiente-H-Cierto: NIL Siguiente-H-Cierto: NIL
Siguiente-H-Falso: NIL Siguiente-H-Falso: NIL

Fig. 4 Estructura de los nodos


(deftemplate nodo
(slot nombre)
(slot tipo (allowed-values intermedio hoja))
(slot afirmacion)
(slot sig-h-cierto)
(slot sig-h-falso))

Por tanto, los nodos se definen como hechos con este formato:
(deffacts arbol-diagnostico
(nodo
(nombre PRLP)
(tipo intermedio)
(afirmación "Prueba de Rumpell-Leede positiva")
(sig-h-cierto VAS)
(sig-h-falso RPB))


(nodo
(nombre TNB)
(tipo intermedio)
(afirmación "Tamaño normal del bazo")
(sig-h-cierto SL)
(sig-h-falso CI))

36
Apuntes de Ingeniería y Gestión del Conocimiento

Para comenzar a recorrer el árbol se termina el def-facts con la definición de un


hecho definiendo el nodo inicial o raíz (en nuestro caso PRLP):
(nodo-actual PRLP))
Lo cual ya prepararía el sistema para una regla como esta:
(defrule afirmacion
?nodo <- (nodo-actual ?nombre))
(nodo (nombre ?nombre)
(tipo intermedio)
(afirmacion ?afirmacion))
(not (respuesta ?))
=> (printout t "¿Es cierto lo siguiente (S/N)? " ?afirmacion)
(assert (respuesta (read))))
Que tiene su continuación en la regla que restringe los valores de la respuesta a
la anterior pregunta:
(defrule respuesta-incorrecta
?respuesta <- (respuesta ~S&~N))
=> (retract ?respuesta))

Al eliminar el hecho basado en respuesta, se activa inmediatamente la regla


afirmación. Por el contrario, si la respuesta fue correcta, o bien se activa:
(defrule respuesta-afirmativa
?nodo <- (nodo-actual ?nombre))
(nodo (nombre ?nombre)
(tipo intermedio)
(sig-h-cierto ?descendiente-cierto))
(?respuesta <- (respuesta S))
=> (retract ?nodo ?respuesta)
(assert (nodo-actual ?descendiente-cierto))

O bien se activa:
(defrule respuesta-negativa
?nodo <- (nodo-actual ?nombre))
(nodo (nombre ?nombre)
(tipo intermedio)
(sig-h-falso ?descendiente-falso))
(?respuesta <- (respuesta N))
=> (retract ?nodo ?respuesta)
(assert (nodo-actual ?descendiente-falso))

Para los nodos hoja:


(defrule nodos-hoja
?nodo <- (nodo-actual ?nombre))
(nodo (nombre ?nombre)
(tipo hoja)
(afirmacion ?afirmacion))

37
Tema 2. Desarrollo de SBCs en Clips

(not (respuesta ?))


=> (retract ?nodo ?respuesta)
(printout t "Se deduce que... " ?afirmacion crlf)
(assert (nuevo-caso))
Se afirma el hecho nuevo-caso para determinar el lanzamiento de la siguiente
regla que pregunta al usuario si desea continuar con otro caso y esto requiere
además otras dos reglas:
(defrule preguntar-por-nuevo-caso
(nuevo-caso)
(not (respuesta ?))
=> (printout t "¿Desea comenzar un nuevo caso (S/N)? ")
(assert (respuesta (read))))

(defrule nuevo-caso
?nuevo-caso <- (nuevo-caso)
?respuesta <- (respuesta S)
=> (retract ?nuevo-caso ?respuesta)
(assert (nodo-actual PRLP)))

(defrule terminar
?nuevo-caso <- (nuevo-caso)
?respuesta <- (respuesta N)
=> (retract ?nuevo-caso ?respuesta)
(printout t "FIN DE LA EJECUCION" crlf))

Si se consideran los nodos intermedios como preguntas sobre el valor de un slot


y se admiten más de dos valores posibles (más allá de un “si” y un “no”), el
algoritmo necesario es algo más complejo, pero también evidente. En la figura se
muestra la trasformación de una parte del ejemplo anterior en un nodo de respuesta
múltiple:
“Tiempo de
tromboplastina parcial
activado?”

normal bajo alto

“Deficiencia del “Deficiencia del “Deficiencia del


FACTOR VII” FACTOR XII, XI, FACTOR X, V,
IX o VIII” Prothrombina o
Fibrinógeno”

Fig. 5 Ejemplo de árbol de decisión de respuesta múltiple


Comienzo
nodo-actual := nodo-raiz;

38
Apuntes de Ingeniería y Gestión del Conocimiento

Mientras not(nodo-hoja(nodo-actual))
Hacer
respuesta:= preguntar(nodo-actual);
i:=1;
Mientras existe-descendiente(nodo-actual, i) y
respuesta <> valor-descendiente(nodo-actual, i)
Hacer
i:= i+1;
Fin
Si existe-descendiente(nodo-actual, i) entonces
nodo-actual := descendiente(nodo-actual, i);
Fin
Visualizar-afirmacion(nodo-actual)
Fin

En el seudocódigo se ha incluido un índice i para referenciar a los enlaces a los


correspondientes nodos descendientes (evidentemente es un procedimiento
sustituible por cualquier otro) en una función de existencia de los dichos enlaces
(existe-descendiente), en una función para obtener la respuesta correspondiente al
enlace i-ésimo (valor-descendiente) y en una función que proporciona justamente
el nodo de ese enlace (descendiente). El algoritmo expuesto asume que la respuesta
del usuario debe coincidir con el valor correspondiente de alguno de los nodos
descendientes. Si se considera la posibilidad contraria, habría que incluir un
procedimiento de vuelta a la pregunta al usuario, para que éste pueda seleccionar
una respuesta para la que sí exista un descendiente.
EJERCICIO 1: Escribir la variante del código anterior de acuerdo a lo
expresado en este seudocódigo.
Una posibilidad muy importante de los árboles de decisión es la posibilidad de
“aprender”, entendiéndose este concepto aquí como la posibilidad de que en la
sesión de trabajo se incluyan nuevos nodos para corregir, completar o para ampliar
el árbol, de tal forma que el árbol quede representado un conocimiento más
completo. Por ejemplo, supongamos el siguiente árbol, previo al de la figura 3, en
el que la afirmación de la hipótesis: “No hay reducción o ausencia de
megacariocito” lleva a la conclusión de “Causas inmunológicas”.

39
Tema 2. Desarrollo de SBCs en Clips

“test de Rumpell Leede


positivo”

Cierto
Falso

“Recuento de
”VASCULITIS” plaquetas bajo”
Cierto
Falso

“No hay reducción o


ausencia de megacariocito” “Tiempo de tromboplastina
parcial activado normal”

Cierto Falso
Cierto Falso

“CAUSAS “APLASIA DE MÉDULA “Tiempo de


INMUNOLOGICAS” ÓSEA ” “Deficiencia del romboplastina parcial
FACTOR VII” activado bajo”

Cierto Falso

“Deficiencia del “Deficiencia del


FACTOR XII, XI, FACTOR X, V,
IX o VIII” Prothrombina o
Fibrinógeno”

Fig. 6 Árbol de decisión incompleto

El usuario experto, durante la sesión de evaluación de un caso, puede


encontrarse ante la secuencia:
> Es cierto que “No hay reducción o ausencia de megacariocitos”?

> Entonces, el origen es de “Causas inmunológicas”
Y, por tanto, desee cambiar la estructura de árbol para que responde
adecuadamente, tal y como muestra la figura 3. Para ello la sesión debería
continuar, ante la exposición de una conclusión:
> Entonces, el origen es de “Causas inmunológicas” ¿Es correcta la
conclusión?
No
>¿Cuál es la conclusión correcta?

40
Apuntes de Ingeniería y Gestión del Conocimiento

Secuestro leucocitario
> Entonces, ¿cuál debería ser la hipótesis cuya afirmación conduce a esta
conclusión?
Tamaño normal del bazo
Con esto, el subárbol que estamos corrigiendo debería quedar así:
“No hay reducción o
ausencia de megacariocito”

Cierto

“Tamaño normal del


bazo”

Cierto
Falso

“SECUESTRO “CAUSAS
LEUCOCITARIO” INMUNOLOGICAS”

Fig. 7 Corrección del árbol de partida

EJERCICIO 2: Escribir la variación del seudocódigo para que incluya


esta posibilidad de aprendizaje.

EJERCICIO 3: Escribir la variante de códigos anteriores para que incluir esta


posibilidad de aprendizaje (nota: habrá que utilizar los operadores load-facts y
save-facts para cargar y guardar las modificaciones en el árbol, en la base de
hechos puesto que los nodos se definen como hechos a partir del template básico).

10 EL PROBLEMA DEL ENCADENAMIENTO HACIA ATRÁS


El motor de inferencia de Clips, como ya se ha nombrado, solo encadena reglas
hacia delante (forward chaining). Esto supone que si tenemos la exigencia de un
encadenamiento hacia atrás (backward chainning) tenemos dos opciones, o utilizar
otra herramienta cuyo motor de inferencia sí pueda hacer este tipo de
encadenamiento, o emular el encadenamiento de alguna forma, sin salir de Clips.
En este apartado vamos a ver esta última opción de un modo simplificado, con
restricciones que iremos detallando. La emulación es tal, como veremos, que no
sirve la construcción def-rule para definir las reglas que van a encadenarse hacia
atrás. Estas se construirán como hechos sobre los que los procedimientos de
encadenamiento realizarán la inferencia.

41
Tema 2. Desarrollo de SBCs en Clips

La estructura de las reglas va a ser muy simple:


(deftemplate rule
(multislot if)
(multislot then))

Por supuesto el slot if sostendrá las premisas mientras que then lo hará con las
conclusiones. En if (o parte izquierda, LHS) determinaremos que las premisas
sencillas tengan la estructura: <atributo> es <valor>, con es como palabra
reservada. Admitiremos esta misma estructura pero entendida como una única
asignación de un valor simple o inclusión de un hecho, como conclusión de la regla
(then o parte derecha, RHS). En la LHS admitiremos la composición a través de la
conectiva y.
<atributo-1> es <valor-1> y
...
<atributo-n> es <valor-n>

Pero el formato Clips de la regla nos interesa especialmente para codificar el


motor de inferencia, pero el seudocódigo, independientemente de este formato, que
expresa las funcionalidad del motor debería ser:
Procedimiento Obtener-objetivo(objetivo)
Si valor(objetivo)<> nil entonces
retornar(valor(objetivo));
Para cada regla ∈ Base-Conocimientos / contiene-rhs(propiedad, regla) hacer
Si regla-con-exito(regla)=TRUE entonces
valor(objetivo):= valor-rhs(regla);
retornar(valor(objetivo))
Fin
Fin
valor(objetivo):= pedir-valor-usuario;
retornar(valor(objetivo);
Fin

El proceso central de este procedimiento es el de búsqueda de las reglas cuya


RHS consistan en la asignación de un valor al atributo objetivo. Como vemos, si no
existe ninguna de tales reglas, el procedimiento pide al usuario que introduzca un
valor para dicho atributo a través de una función pedir-valor-usuario. En el
seudocódigo se incluye una estructura de control que recorre todas las reglas, las
incluidas en la base de conocimientos que, recordemos, se ha construido como un
conjunto de hechos. Se asume también la existencia de un función que identifica la
existencia del atributo objetivo en la parte derecha de la regla RHS (contiene-rhs),
así como de una función de devuelve un TRUE si la regla cumple todas las
premisas de su LHS (regla-con-éxito). Y para esto último, para la función regla-
con-exito, el siguiente seudocódigo puede ser descriptivo:
Procedimiento Regla-con-exito(regla)

42
Apuntes de Ingeniería y Gestión del Conocimiento

Para cada condición ∈ LHS(regla) hacer


Si Obtener-objetivo(propiedad(condicion))<> valor(condicion) entonces
retornar(FALSE);
Fin
retornar(TRUE);
Fin

Este procedimiento, claro, hace uso recursivamente del procedimiento


invocante obtener-objetivo esta vez para buscar un valor que permite evaluar el
cumplimiento de la condición de la parte izquierda de la regla.
Para implementar este mecanismo de encadenamiento hacia atrás en Clips, a
partir del formato de regla simplificado que ya hemos definido es preciso definir un
template para describir los hechos que juegan el papel de objetivos, a través de la
definición de una propiedad, en la búsqueda y otro para describir los hechos de los
valores de esas propiedades realmente encontrados (propiedad-valor).
(deftemplate objetivo
(slot propiedad))

(deftemplate propiedad-valor
(slot propiedad)
(slot valor))

Además es preciso definir un hecho que soporte el objetivo inicial de la


búsqueda. El objetivo es el atributo cuyo valor se busca en la inferencia:
(deffacts objetivo-inicial
(objetivo (propiedad propiedad-objetivo-inicial))
Como problema ejemplo, vamos a utilizar una versión simplificada del
diagnóstico de hemorragias:

43
Tema 2. Desarrollo de SBCs en Clips

“test de Rumpell Leede


positivo”

Cierto
Falso

“Recuento de
”VASCULITIS” plaquetas bajo”
Cierto
Falso

“No hay reducción o “Deficiencia de


ausencia de megacariocito” factores de
coagulación”
Cierto Falso

“Tamaño normal del “APLASIA DE MÉDULA


bazo” ÓSEA ”

Cierto
Falso

“SECUESTRO “CAUSAS
LEUCOCITARIO” INMUNOLOGICAS”

Fig. 8 Árbol de decisión del ejemplo simplificado

La reglas que soportan este árbol serían las siguientes:


(deffacts reglas-diagnostico
(rule (if rumpell-leede es positivo)
(then diagnostico es vasculitis))

(rule (if rumpell-leede es negativo y


recuento-plaquetas es normal)
(then diagnostico es deficiencia-factores-coagulacion))
(rule (if rumpell-leede es negativo y
recuento-plaquetas es bajo y
reduccion-megacariocitos es falso)
(then diagnostico es aplasia-medula-osea))
(rule (if rumpell-leede es negativo y
recuento-plaquetas es bajo y
reduccion-megacariocitos es cierto y
dimension-bazo es normal)
(then diagnostico es secuestro-leucocitario))
(rule (if rumpell-leede es negativo y
recuento-plaquetas es bajo y
reduccion-megacariocitos es cierto y

44
Apuntes de Ingeniería y Gestión del Conocimiento

dimension-bazo es anormal)
(then diagnostico es causas-inmunologicas)))
Junto con los hechos iniciales:
(propiedad-valor (propiedad rumpell-leede))
(propiedad-valor (propiedad recuento-plaquetas))
(propiedad-valor (propiedad reduccion-megacariocitos))
(propiedad-valor (propiedad dimension-bazo))

Por tanto, el objetivo inicial es encontrar el valor del diagnóstico:


(deffacts objetivo-inicial
(objetivo (propiedad diagnostico))
Para ello, debemos incluir dos reglas clips, una que busque las rules cuya
conclusión (then) incluya la asignación de un valor a diagnóstico y que afirme su
antecedente (if) como nuevo objetivo, siempre y cuando no tenga ya un valor
previo o ya sea un objetivo.
(defrule evaluar-regla
(objetivo (propiedad ?objetivo))
(rule (if ?propiedad $?)
(then ?objetivo $?))
(not (propiedad-valor (propiedad ?propiedad)))
(not (objetivo (propiedad ?propiedad)))
=>
(assert (objetivo (propiedad ?propiedad))))

Y la otra que pregunte al usuario por el valor del antecedente cuando no haya
rules que lo incluyan en su consecuente.
(defrule pedir-valor-propiedad
?hecho-objetivo <- (objetivo (propiedad ?objetivo))
(not (propiedad-valor (propiedad ?objetivo)))
(not (rule (then ?objetivo $?)))
=>
(retract ?hecho-objetivo)
(printout t "Introduzca el valor de " ?objetivo ": ")
(assert (propiedad-valor (propiedad ?objetivo)
(valor (read)))))
Complementando a estas dos reglas, debe haber otra regla que elimine como
objetivo aquél que ya se ha logrado y esto, es evidente, debe tener prioridad sobre
la búsqueda de objetivos (las anteriores reglas):
(defrule retractar-objetivo-logrado
(declare (salience 10))
?hecho-objetivo <- (objetivo (propiedad ?objetivo))
(propiedad-valor (propiedad ?objetivo))
=>

45
Tema 2. Desarrollo de SBCs en Clips

(retract ?hecho-objetivo))
También tiene que tener prevalencia la afirmación del consecuente de una rule
cuando su antecedente, como anterior objetivo, se haya logrado. Esto tiene que
llevar asociado la eliminación de esa rule.
(defrule afirmar-consecuente
(declare (salience 10))
(objetivo (propiedad ?objetivo))
(propiedad-valor (propiedad ?propiedad)
(valor ?valor))
?rule <- (rule (if ?propiedad es ?valor)
(then ?objetivo es ?valor-objetivo))
=>
(retract ?rule)
(assert (propiedad-valor (propiedad ?objetivo)
(valor ?valor-objetivo))))

Cuando se cumple la primera premisa del antecedente de una rule, esta misma
premisa se elimina de la rule, para no volverla a tener en cuenta:
(defrule eliminar-premisa-cumplida
(declare (salience 10))
(objetivo (propiedad ?objetivo))
(propiedad-valor (propiedad ?propiedad)
(valor ?valor))
?rule <- (rule (if ?propiedad es ?valor y $?resto-if)
(then ?objetivo es ?valor-objetivo)))
=>
(modify ?rule (if $?resto-if)))

Pero si no se cumple la premisa, y por tanto, todo el antecedente, toda la rule


debe ser eliminada:
(defrule retractar-rule-fallida
(declare (salience 10))
(objetivo (propiedad ?objetivo))
(propiedad-valor (propiedad ?propiedad)
(valor ?valor))
?rule <- (rule (if ?propiedad es ~?valor)
(then ?objetivo es ?valor-objetivo)))
=>
(retract ?rule))
Si deseamos que en la pregunta al usuario se especifiquen los posibles valores
aceptables, una forma muy sencilla de resolverlo sería modificar la regla pedir-
valor-propiedad para que quede:
(defrule pedir-valor-propiedad
?hecho-objetivo <- (objetivo (propiedad ?objetivo))

46
Apuntes de Ingeniería y Gestión del Conocimiento

(not (propiedad-valor (propiedad ?objetivo)))


(not (rule (then ?objetivo $?)))
(valores-posibles (propiedad ?objetivo) (valor-1 ?valor-1) (valor-2 ?valor-2))
=>
(retract ?hecho-objetivo)
(printout t "Introduzca el valor de " ?objetivo " (" ?valor-1 "/" ?valor-2
"): ")
(assert (propiedad-valor (propiedad ?objetivo)
(valor (read)))))

Para lo que es preciso definir un nuevo template:


(deftemplate valores-posibles
(slot propiedad)
(slot valor-1)
(slot valor-2))
Y los siguientes hechos complementarios:
(deffacts pruebas
(valores-posibles (propiedad rumpell-leede) (valor-1 positivo) (valor-2 negativo))
(valores-posibles (propiedad recuento-plaquetas) (valor-1 normal) (valor-2 bajo))
(valores-posibles (propiedad reduccion-megacariocitos) (valor-1 cierto) (valor-2
falso))
(valores-posibles (propiedad dimension-bazo) (valor-1 normal) (valor-2 anormal)))

11. ORIENTACIÓN A OBJETOS


Objeto: Colección de información (los datos) que representa una entidad del
mundo real y la forma de manipularla (el código asociado). Los objetos vienen
descritos por:
Propiedades: Especificadas en términos de los atributos (slots) de la clase del
objeto.
Conducta: Especificada en términos de código procedural (gestores de
mensajes) asociado a la clase del objeto.
Los objetos se manipulan mediante el paso (envío) de mensajes.
Principios de la OOP
Abstracción: Representación intuitiva de mayor nivel para un concepto
complejo. Definición de nuevas clases para describir las propiedades
comunes y el comportamiento de un grupo de objetos.
Encapsulación: Mecanismo que permite la ocultación de los detalles de
implementación de un objeto mediante el uso de un interfaz externo bien
definido.
47
Tema 2. Desarrollo de SBCs en Clips

Herencia: Proceso por el cual una clase puede ser definida en términos de
otra(s) clase(s).
Polimorfismo: Capacidad de diferentes objetos para responder al mismo
mensaje de una forma especializada.
Ligadura dinámica (dynamic binding): Capacidad para seleccionar en
tiempo de ejecución el gestor de mensajes que corresponde a un mensaje
concreto.
Definición de Clases:
Construcción:
(defclass <nombre> [<comentario>]
(is-a <nombre-de-superclase>+) ;;; indica un orden de herencia
[(role concrete | abstract)];;; puede instanciarse o no
[(pattern-match reactive | non-reactive)];;; admite machting en una regla o no
<slot>* ;;; definición de los atributos de la clase
<documentación-handler>*
)
Herencia múltiple
COOL examina cada nueva clase utilizando la lista de precedencia de clases (de
superclases) para establecer un orden lineal y le hace heredar slots y gestores
(conducta) de cada una de las clases de esta lista en orden. Tiene prioridad las
primeras clases de la lista (las más específicas) para heredar si hay conflicto (tienen
el mismo nombre).
Definición de slots:
(describe-class <nombre-de-clase>)
En la cláusula is-a ya se establece una precedencia
(slot <nombre> <facet>*)
(single-slot <nombre> <facet>*)
(multislot <nombre> <facet>*)
facets: son las propiedades de los atributos, las cuales son ciertas para todos los
objetos que comparten dichos slots.

48
Apuntes de Ingeniería y Gestión del Conocimiento

(default ?DERIVE | ?NONE | <expresión>*) ;;; se evalúa al crear la clase


(default-dynamic <expresión>*) ;;; se evalúa al instanciar
(storage local | shared) ;;; el valor se almacena en la instancia o en la clase
(access read-write | read-only | initialize-only) ;;; tipo de acceso permitido
(propagation inherit | no-inherit) ;;; heredable o no heredable
(visibility private | public) ;;; solo permite el acceso a los gestores propios (no
heredados)
(source exclusive | composite) ;;; hereda la facet de la clase más espcífica o de
las anteriores
(pattern-match reactive | non-reactive) ;;; cambios activan reglas o no
(create-accessor ?NONE | read | write | read-write) ;;; crea automáticamente
los gestores
restricciones (type ...) (range ..) (cadinality ..)
Pattern-matching con objetos.
Las modificaciones en atributos no reactivos o en instancias de clases no
reactivas no tendrán efecto alguno en las reglas.
(object <atributo-de-restricción>) ;;; donde
<atributo-de-restricción> ::=
(is-a <restricción>) | ;;; clase a la que pertenece
(name <restricción>) | ;;; nombre de instancia entre []
(<nombre-atributo> <restricción>*) ;;; igual que con templates

Ejemplo: "Apaga la caldera si la habitación está vacía"


(defrule UNIDAD-CONTROL::UC7
(object (is-a Termostato) (temperatura ?t)
(temperatura-seleccionada ?ts & :(>= ?t (- ?ts 3)))
(caldera ?c) (modo calor) (habitación ?h))
?inst <- (object (is-a Caldera) (name ?c)

49
Tema 2. Desarrollo de SBCs en Clips

(estado encendida))
(object (is-a Habitacion) (name ?h) (ocupación vacía))
=>
(send ?inst Apagar)
)

Paso de mensajes:
Los objetos se manipulan mediante el envío de mensajes utilizando la función
send.
La implementación de un mensaje consta de una serie de fragmentos de
código procedural llamados gestores de mensajes (message-handlers). El
valor devuelto por un gestor de mensajes es el resultado de la evaluación
de la última acción.
La construcción defmessage-handler se utiliza para especificar la conducta de
los objetos de una clase en respuesta a un mensaje particular.
Dentro de una clase, los gestores para un mensaje particular pueden ser de
diferentes tipos diferentes: Primary (el proceso principal), Before, After.

(defmessage-handler <nombre-clase> <nombre-mensaje> [<tipo-handler>]


;;; que identifican al mensaje
[<comentario>]
(<parámetro>* [<parámetro-comodín>]) <acción>*)

(defmessage-handler rectángulo calcular-área ()


(* ?self:base ?self:altura)) ;;; Devuelve el area (base por altura).
(defmessage-handler rectangulo imprimir-area ()
(printout t (send ?self calcular-area) crlf))

Demonios y mensajes predefinidos

50
Apuntes de Ingeniería y Gestión del Conocimiento

Demonios: Son fragmentos de código que se ejecutan implícitamente cuando


se realiza alguna acción básica sobre una instancia como son la
inicialización, borrado y la lectura y escritura de atributos, que han sido
implementados como gestores primarios asociados a la clase de la
instancia.
Gestores de mensajes predefinidos:
init: inicializa una instancia con sus valores por defecto después de crearla.
make-instance) e inicializar instancias (initialize-instance) envían
automáticamente el mensaje init.
delete: devuelve TRUE si logra borrar con éxito la instancia; devuelve FALSE
en caso contrario.
print: visualiza en pantalla los atributos y sus valores de una instancia dada.
...
Cuando un objeto recibe un mensaje mediante la función send, CLIPS examina
la lista de precedencia de clases para determinar el conjunto de gestores de
mensajes aplicables al mensaje.
Un gestor de mensajes es aplicable a un mensaje, si su nombre coincide con el
mensaje y el gestor está ligado (asociado) a alguna clase de la lista de
precedencia a la cual pertenezca la instancia que recibe el mensaje.

Manipulación de instancias
Los objetos se manipulan mediante el envío de mensajes de unos a otros.
(send <expresión-de-objeto> <nombre-de-mensaje>
<expresión>*)
La función send devuelve el valor del último gestor (primary o around)
ejecutado.

(make-instance [<nombre-instancia>] of <nombre-clase>


(<nombre-slot> <expresión>)*)

(definstances <nombre-colección-instancias>

51
Tema 2. Desarrollo de SBCs en Clips

[active] [<comentario>]
(<definición-de-instancia>)*)

get-<nombre-atributo>
put-<nombre-atributo>
delete

Consultas sobre instancias:


any-instancep Determina si uno o más conjuntos de instancias satisfacen una
consulta.
(any-instacep ((?hombre HOMBRE)) (> ?hombre:edad 30))
find-instance Devuelve el primer conjunto de instancias que satisfaga una
consulta.
(find-instance ((?h HOMBRE) (?m MUJER)) (= ?h:edad ?m:edad))
find-all-instances Agrupa y devuelve todos los conjuntos de instancias que
satisfacen una consulta.
(find-all-instances ((?h HOMBRE) (?m MUJER)) (= ?h:edad ?m:edad))
do-for-instance Realiza una acción sobre el primer conjunto de instancias que
satisfaga una consulta.
(do-for-instance ((?p1 PERSONA) (?p2 PERSONA) (?p3 PERSONA))
(and (= ?p1:edad ?p2:edad ?p3:edad)
(neq ?p1 ?p2) (neq ?p1 ?p3) (neq ?p2 ?p3))
(printout t ?p1 " " ?p2 " " ?p3 crlf))
do-for-all-instances Realiza una acción con cada conjunto de instancias que
satisface una consulta según se van encontrando.
(do-for-instance ((?p1 PERSONA) (?p2 PERSONA) (?p3 PERSONA))
(and (= ?p1:edad ?p2:edad ?p3:edad)
(neq ?p1 ?p2) (neq ?p1 ?p3) (neq ?p2 ?p3))

52
Apuntes de Ingeniería y Gestión del Conocimiento

(printout t ?p1 " " ?p2 " " ?p3 crlf))

Realizar los de COOL

53

Das könnte Ihnen auch gefallen