Reglas
Es posible no definir ningún elemento condicional en el antecedente de una regla, situación en la que se usa automáticamente (initial-fact), el hecho que se añade de forma automática tras ejecutar el comando (reset), como elemento condicional.
También puede no haber ninguna acción en el consecuente, con lo que la ejecución de la regla no tiene en ese caso ninguna consecuencia.
En CLIPS pueden realizarse comentarios colocándolos detrás de un punto y coma (;). Todos los caracteres desde el punto y coma hasta el siguiente salto de línea serán considerados comentarios.
Reglas
La condición o antecedente de una regla puede ser múltiple:
(defrule cielo
(color azul)
(estamos-en exterior)
=>
(assert (cielo-es despejado)))
Al igual que la acción resultante, p.e. para mostrar un mensaje por la salida estándar con printout:
(defrule cielo
(color azul)
(estamos-en exterior)
=>
(assert (cielo-es despejado))
(printout t “cielo despejado" crlf) )
Recuerda utilizar reset para limpiar la base de hechos antes de volver a ejecutar o de lo contrario las reglas no se activarán
Reglas
Como probablemente habrás pensado, no es buena política teclear cada vez todas tus reglas, puedes tenerlas grabadas y cargarlas utilizando File->Load para cargar un fichero clp
Desde la consola se carga un fichero clp por medio del comando load, por ejemplo:
(load “nombre-fichero.clp”)
Se puede almacenar la información actual de los constructores con save, o hacer uso de la interfaz para ello, por ejemplo:
(save “nombre-fichero.clp”)
Reglas
Ejercicios:
Define una regla
Lista las reglas existentes
Define una nueva regla con el mismo nombre. ¿Qué ocurre?
Define otra regla
Lista las reglas existentes
Muestra el contenido de una regla
Elimina una regla
Primeros ejemplos: Hechos iniciales
Es frecuente tener que utilizar el mismo conjunto de hechos en cada ejecución del programa, equivalente a variables iniciales en un esquema de programación imperativo.
El constructor deffacts permite añadir conocimiento en forma de hechos iniciales, tanto ordenados como no ordenados. Los hechos iniciales definidos, se incluyen en la base de hechos tras un reset de similar forma al hecho por defecto initial-fact.
Por ejemplo:
(deffacts arranque (color azul) (color rojo))
(deffacts gente (datos–persona (nombre Samuel) (edad 20) (peso 80) (altura 188) (presion-arterial 130 80)) (datos-persona (nombre Mar) (edad 63) (peso 70) (altura 167) (presion-arterial 180 90)))
Primeros ejemplos: Hechos iniciales
Los hechos así añadidos se tratan como cualquier otro.
Pueden definirse varios grupos de hechos iniciales, recibiendo cada uno de ellos un identificador al declararse.
El identificador agrupa conceptualmente diferentes hechos sobre el mismo objeto:
(deffacts coche
“información del coche”
(marca Seat)
(modelo Ibiza)
(puertas 5) )
Primeros ejemplos: Hechos iniciales
Como con otros constructores, (list-deffacts) muestra los grupos de hechos iniciales definidos con deffacts, (ppdeffacts ) nos permitirá mostrar el contenido de uno de ellos, y (undeffacts ) suprime los hechos insertados por una orden (deffacts).
CLIPS> (list-deffacts)
initial-fact
arranque
For a total of 2 deffacts.
CLIPS> (ppdeffacts arranque)
(deffacts MAIN:: arranque
(color azul)
(color rojo))
Reglas: Hechos iniciales
Ejercicios:
Define unos hechos iniciales
Define alguna regla que haga uso no sólo de estos hechos iniciales
Añade los hechos necesarios para activar la regla
Prueba a ejecutar varias veces
Lista las definiciones de hechos iniciales
Muestra el contenido de una de ellas
Primeros ejemplos: Comodines
Los hechos utilizados hasta ahora en las reglas eran muy simples, al estar referidos a hechos específicos. Los comodines permiten especificar reglas que encajen para distintos hechos. Definamos los hechos iniciales y regla siguientes:
CLIPS> (deffacts arranque (animal perro) (animal gato) (animal pato))
CLIPS> (defrule animal
(animal ?)
=>
(printout t "animal encontrado" crlf) )
CLIPS> (run)
animal encontrado
animal encontrado
animal encontrado
? es un comodín que encaja con cualquier símbolo de un hecho, en este caso el segundo. Nunca puede colocarse en el primer lugar, es por ello válido (padre_de ? ?) pero no (? Juan ?)
Primeros ejemplos: Variables
Las variables permiten ofrecer aún mayor flexibilidad, ya que ofrecen la posibilidad de ser reutilizadas en la propia regla. Para ello utilizamos ?nombre-var. Un ejemplo es la siguiente regla:
(defrule lista-animales
(animal ?nombre)
=>
(printout t ?nombre “ encontrado" crlf))
CLIPS> (reset)
CLIPS> (run)
animal encontrado
pato encontrado
animal encontrado
gato encontrado
animal encontrado
perro encontrado
Observa que también se activa la regla anterior, animal, porque no la hemos eliminado
Primeros ejemplos: Variables
Pueden combinarse el uso de una variable en varias de las condiciones o patrones de una regla. Para ver un ejemplo redefinimos los hechos por defecto con deffacts y tras eliminar todas las reglas, con (clear), introducimos una nueva:
CLIPS> (deffacts arranque (animal perro) (animal gato) (animal pato) (animal tortuga) (sangre-caliente perro) (sangre-caliente gato) (sangre-caliente pato) (pone-huevos pato) (pone-huevos tortuga))
CLIPS> (defrule mamifero
(animal ?nombre) (sangre-caliente ?nombre) (not (pone-huevos ?nombre))
=>
(assert (mamifero ?nombre))
(printout t ?nombre " es un mamífero" crlf))
CLIPS> (run)
gato es un mamífero
perro es un mamífero
Primeros ejemplos: Variables
Pueden aparecer varias variables en la condición:
(deffacts arranque (animal perro) (animal gato) (animal pato) (animal tortuga) (sangre-caliente perro) (sangre-caliente gato) (sangre-caliente pato) (pone-huevos pato) (pone-huevos tortuga) (hijo-de perro perrito) (hijo-de gato gatito) (hijo-de pato patito))
(defrule mamifero2
(mamifero ?nombre) (hijo-de ?nombre ?joven)
=>
(assert (mamifero ?joven))
(printout t ?joven " es un mamífero " crlf))
CLIPS> (run)
gatito es un mamífero
perrito es un mamífero
Primeros ejemplos: Variables
Sabemos que podemos eliminar un hecho por su índice con retract. Es posible hacerlo en una acción tras conectarlo en una regla con una variable
(defrule elimina
?hecho <- (mamifero ?nombre)
=>
(printout t "eliminando “ ?hecho crlf) (retract ?hecho))
El operador <- almacena la referencia o índice al hecho en la variable ?hecho y la acción recoge esa variable para realizar el retract
CLIPS> (run)
gato es un mamífero
eliminando
perro es un mamífero
eliminando
Primeros ejemplos: Variables
Las variables también pueden emplearse en el consecuente de una regla por medio de la función bind que asigna un valor a una variable, sirviendo ésta como variable temporal:
(defrule suma
(num ?x) (num ?y)
=>
(bind ?total (+ ?x ?y))
(printout t ?x " + " ?y " = “ ?total crlf)
(assert (total ?total)))
En este caso es una variable temporal que sólo tiene validez dentro de esa regla.
Primeros ejemplos: Variables
Si se necesitan variables globales, es decir para usar en más de una regla, empleamos defglobal, estas variables deben nombrarse delimitadas por *:
(defglobal
?*var1* = 17 ?*naranjas* = “siete" )
Tras realizar un reset retoman su valor original. (get-defglobal-list) permite obtener la lista, (show-defglobals) también su contenido, (undefglobal var1) elimina la variable var1.
Como el resto de variables, pueden modificar su valor mediante la función bind
(defrule prueba
(dato ?)
=> (bind ?*var1* 3))
Primeros ejemplos: Condiciones
test permite establecer condiciones que van más allá de los hechos al permitir hacer uso de variables y valores numéricos
(defrule nonecesario
(test (> 6 5))
=>
(printout t “Seis es mayor que cinco" crlf) )
(defrule positivo
(valor ?val)
(test (> ?val 0))
=>
(printout t ?val “es positivo" crlf) )
Primeros ejemplos: Variables y Condiciones
Ejercicios:
Escriba una regla que se active ante hechos del tipo (valor a b)
Escriba una regla que se active con hechos del tipo (valor a b), siempre que b sea mayor que a, circunstancia en la que se escribirán los valores de a y b en pantalla. Comprueba el funcionamiento con la siguiente base de hechos:
(valor 6 12)
(valor 6 7)
(valor 15 30)
(valor 14 7)
Primeros ejemplos: Entrada y salida
La función read permite a CLIPS leer información proporcionada por el usuario. El programa se detiene cuando, a la espera de que el usuario teclee el dato.
Puedes probar con (assert (milectura (read))) que tras leer el dato añade un hecho. Puedes comprobarlo con (facts).
La siguiente regla pregunta por un dato si no está ya disponible en la base de hechos:
(defrule estan-luces-funcionando
(not (luces-funcionando ?))
=>
(printout t "¿Están las luces del coche funcionando (S o N)?")
(assert (luces-funcionando (read))))
Si no existe ya un hecho con el símbolo luces-funcionando, el sistema preguntará al usuario por el dato.
Primeros ejemplos: Entrada y salida
Podemos controlar que la respuesta no siga el formato esperado haciendo
(defrule r3
?hecho <- (luces-funcionando ?)
(not (luces-funcionando S))
(not (luces-funcionando N))
=>
(printout t “Formato de respuesta incorrecto")
(retract ?hecho))
Primeros ejemplos: Entrada y salida
La función read lee sólo hasta el primer espacio en blanco. Si queremos introducir una serie de elementos, separados por espacios, debemos utilizar readline
(defrule obtener-nombre
=>
(printout t "Su nombre: ")
(assert (nombre (readline))))
La función readline retorna una cadena. Evitamos que sea una cadena y se considere un conjunto de símbolos con explode$
(defrule obtener-nombre2
=>
(printout t "Su nombre: ")
(assert (nombre (explode$(readline)))))
Primeros ejemplos: Entrada y salida
Ejercicios:
Escribir una base de conocimiento CLIPS que lea la respuesta (positiva o negativa a una pregunta) y posteriormente escriba si la respuesta ha sido positiva o negativa.
Escriba una base de conocimiento CLIPS que tras leer una secuencia de tres números enteros, escriba la secuencia si el tercer número es la suma de los dos primeros.
Nota: Se recomienda usar la función (readline) para leer una cadena de entrada, y seguidamente convertirla en un multivalor mediante la función (explode$)
Primeros ejemplos: Entrada y salida
El comando printout permite realizar operaciones tanto de escritura por pantalla como a fichero
(printout )
stdout Nombre lógico de la salida por pantalla, suele sustituirse por t
CLIPS> (printout t “¡Hola!!" crlf)
¡Hola!
crlf al final establece un fin de línea
Primeros ejemplos: Entrada y salida
La escritura a fichero con el comando printout, modifica el nombre lógico.
Previamente debe abrirse el fichero (modo escritura w, lectura r, lectura y escritura r+, y apéndice a). Recuerda indicar la ruta correctamente
(open "mifichero.txt" F1 "w“) ;abre en modo escritura indicando w
(printout F1 “dato 23") ; escribe datos en fichero
(close F1) ; cierra el fichero
Primeros ejemplos: Entrada y salida
El comando read lee del fichero indicado un solo campo. Para leer una línea empleamos readline
(open "mifichero.txt" F1) ; indicando r o nada abre en modo lectura
(read F1) ; lee hasta primer espacio
(read F1) ; lee hasta siguiente espacio
(close F1) ; cierra fichero
stdin Nombre lógico de la entrada por defecto empleada por read y readline.
Existen también comandos como rename y remove que permiten respectivamente renombrar y eliminar un fichero
Primeros ejemplos: Entrada y salida
Un ejemplo de lectura y escritura
CLIPS> (open "data.txt" misdatos "w")
TRUE
CLIPS> (printout misdatos “lunes martes")
CLIPS> (close)
TRUE
CLIPS> (open "data.txt" misdatos)
TRUE
CLIPS> (readline misdatos)
"lunes martes"
CLIPS> (readline misdatos)
EOF
CLIPS> (close)
TRUE
Primeros ejemplos: Estructuras de control
Sentencia condicional
( if ()
then ()
[else ()] )
Sentencia repetitiva o bucle
(loop-for-count ( ) [do] )
(while () [do] () )
Los elementos entre corchetes son opcionales
Primeros ejemplos: Estructuras de control
(defrule continua-bucle
?bucle <- (bucle)
=>
(printout t “¿Continuar?” crlf)
(bind ?resp (read))
(if (or (eq ?resp si)(eq ?resp s))
then
(retract ?bucle) ; Ante una respuesta positiva quitamos y
(assert (bucle)) ; añadimos el hecho
else
(retract ?bucle) ; No continuamos, simplemente eliminamos
(halt)
) )
Primeros ejemplos: Estructuras de control
Prueba los bucles con:
(loop-for-count 2 (printout t "Hola mundo" crlf))
(loop-for-count (?i 0 2) do
(loop-for-count (?j 1 3) do
(printout t ?i " " ?j crlf)))
(defrule rwhile
=>
(bind ? 4)
(while (> ?v 0) ; antes ?v debe tener un valor
(printout t “v es " ?v “ " crlf)
(bind ?v (- ?v 1))) )
Primeros ejemplos: Estructuras de control
Ejercicios
Realizar un programa CLIPS que tras leer un número indique si es positivo o negativo empleando la estructura condicional
Realizar un programa en CLIPS que tras leer un número entero escriba todos los valores enteros entre 0 y ese número
Tras almacenar un número en una variable global, se pide realizar un programa en CLIPS que permita al usuario introducir números enteros entre 1 y 100 hasta que adivine el número que el sistema tiene almacenado. La entrada de números se hará mediante la sentencia read. El programa indicará por pantalla si el número buscado es mayor o menor que el introducido. La condición de parada se produce cuando el usuario acierte el número en cuestión.
Nota: Para generar números aleatorios usa:
(random [ ])
(seed (round (time))) ;modifica la semilla
Primeros ejemplos: Operadores lógicos y matemáticos
Dos patrones en una regla se conectan de forma automática con un and, ya que requiere que ambos se cumplan para que la regla se dispare. Este ejemplo, visto anteriormente, incluye el uso del not
(defrule mamifero
(animal ?nombre) (sangre-caliente ?name) (not (pone-huevos ?nombre))
=>
(assert (mamifero ?nombre))
(printout t ?nombre " es un mamífero" crlf))
Nos faltaría ver un ejemplo de uso de or:
(defrule coge-paraguas
(or (tiempo lloviendo) (tiempo nevando)) => (assert (paraguas necesario)))
Que traducimos por “Si está lloviendo o nevando, coge el paraguas”, haciendo uso de sólo una regla ya que también podrían utilizarse dos independientes con el mismo efecto.
Primeros ejemplos: Operadores lógicos y matemáticos
Observa que el operador or está colocado antes de los argumentos y no entre ellos, es la denominada notación prefijo (prefix notation) y la utilizan todos los operadores en CLIPS.
Para por ejemplo realizar una suma, la mayoría de los lenguajes, usarían 5 + 7 (infix notation), mientras que CLIPS requiere (+ 5 7).
Algunos ejemplos:
CLIPS>(+ 5 7)
12
CLIPS>(- 5 7)
-2
CLIPS>(* 5 7)
35
CLIPS>(/ 5 7)
0.7142857142857143
Expresiones más complicadas como 10+4*19-35/12:
CLIPS> (+ 10 (- (* 4 19) (/ 35 12)))
Primeros ejemplos: Funciones
Una función es un algoritmo identificado con un nombre que puede o no devolver valores (uni o multicampo). Se definen con el constructor deffunction, existiendo dos tipos:
Funciones internas: Definidas en CLIPS
Funciones externas: Escritas en un lenguaje distinto a CLIPS
(deffunction signo (?num)
(if (> ?num 0) then (return 1))
(if (< ?num 0) then (return -1))
0)
Las funciones se llaman usando la notación prefija entre paréntesis como ya hemos visto con los operadores aritméticos y lógicos:
CLIPS> (signo 3)
1
El comando list-deffunctions permite listar las funciones definidas
Primeros ejemplos: Funciones
Cuando una función se llama a sí misma hablaremos de recursividad:
(deffunction sumatorio (?num)
( if (> ?num 0) then (return (+ ?num (sumatorio (- ?num 1))))
else 0) )
CLIPS> (sumatorio 5)
15
Elementos básicos: Funciones
Ejercicios
Escribe una función que calcule el factorial de un número
Escribe función en CLIPS para calcular y mostrar por pantalla el máximo común divisor (mcd) de dos números obtenidos a través del teclado, asumiendo que son positivos. El mcd de estos números se calcula teniendo en cuenta:
Si a = b mcd( a, a ) = a
Si a > b mcd( a, b ) = mcd( a-b, b )
Si a < b mcd( a, b ) = mcd( a, b-a )
Elementos básicos: Funciones
Ejercicios
El mínimo común múltiplo (mcm) de dos números enteros a y b se puede calcular a partir del máximo común divisor (mcd) de dichos números tal que mcm(a,b)=a*b /(mcd(a,b)). Escribe una función en CLIPS para calcular el mcm de dos números enteros que siga los siguientes pasos:
Inicialmente se leen dos números a y b del teclado
Seguidamente se calcula el mcd de estos números
Finalmente calcula el mcm y se muestra el resultado en pantalla
Página anterior | Volver al principio del trabajo | Página siguiente |