Mostrando entradas con la etiqueta JavaScript. Mostrar todas las entradas
Mostrando entradas con la etiqueta JavaScript. Mostrar todas las entradas

Diseño de un motor de reglas en JavaScript (Parte – IV final)


Ahora vamos a tratar como invocar desde una simple aplicación JavaScript el motor de reglas.

Para este ejemplo, hemos colocado todas las reglas, hechos y preguntas en un archivo al que llamaremos rules.js y formará parte de nuestro sitio Web.

De manera muy básica solo tenemos que cambiar reglas en este archivo y actualizarlo para que el motor pueda encontrar soluciones de una manera u otra.

También se puede persistirse este objeto JSON (JavaScript Object Notation) en una base de datos remota y tener una aplicación que permita construir y mantener la información depositada en la base de conocimientos.

Lo primero es ver como esta organizado el motor:

Archivo
Descripción
Question.js
Clase que define a las preguntas
Fact.js
Clase que define a los hechos
Rule.js
Clase que define las reglas
REE.js
Motor de evaluación de reglas

Por lo tanto, en nuestra aplicación Web deberíamos incluir los archivos de esta manera:
<!-- business rules -->

<script src="Rules.js" type="text/javascript"></script>

<!-- rules evaluation engine -->

<script src="Question.js" type="text/javascript"></script>
<script src="Fact.js" type="text/javascript"></script>
<script src="Rule.js" type="text/javascript"></script>
<script src="REE.js" type="text/javascript"></script>

Luego tenemos que construir una instancia del motor.

ree = new RulesEvaluationEngine(knowledge,
                 askCallback,
                 solutionCallback,
                 logicaCallback,true);

Los parámetros de esta llamada son:

  • La base de conocimientos en formato JSON
  • La función callback para realizar las preguntas
  • La función callback para presentar la solución
  • La función callback para presentar la lógica de la solución (esta versión del motor solo incluye hechos y preguntas. Las reglas no se incluyen)
  • Un parámetro booleano que indica si el motor se ejecuta paso a paso o en modo de evaluación completa

El motor tiene dos formas de ejecutarse:

Forma de ejecución
Descripción
Paso a paso
El motor va generando las preguntas necesarias según las respuestas a preguntas anteriores
Evaluación completa
El usuario responde todas las preguntas y luego el motor evalúa todos los resultados.

La comunicación entre la aplicación y el motor se realiza a través de tres funciones callback:

Callback function
Descripción
askCallback
Realiza las preguntas
solutionCallback
Presenta los resultados de la evaluación de las reglas
logicaCallback
Devuelve los hechos establecidos y las respuestas del usuario para aclarar como se llegó a una conclusión

Antes de ejecutar el motor en cualquiera de los modos, tenemos que iniciar la evaluación, proceso que elimina todas las respuestas y todos los hechos establecidos de una evaluación anterior:

ree.initEvaluation();

Para evaluar las preguntas, si estamos utilizando el modo de evaluación completa tenemos dos funciones para navegar y responder en el conjunto de preguntas:

ree.Next();
ree.Prev();

Estas funciones tienen que combinarse con la función Evaluate() que realmente invoca al motor para obtener las conclusiones. En el caso de la evaluación paso a paso, en cada paso se realizan evaluaciones o inferencias en el motor hasta encontrar una solución.

En el caso de evaluación paso a paso tenemos la función Step:

ree.Step();

Por último, la función Logica() devuelve el conjunto de preguntas y hechos establecidos durante la evaluación.

ree.Logica();

En el ejemplo utilizaremos las últimas versiones de jQuery y jQuery Mobile para construir una sencilla interfaz de usuario con tres botones:

  • El primero para iniciar el motor (poner condiciones iniciales)
  • El segundo para realizar preguntas
  • El tercero para mostrar la lógica de la solución

Limitaciones

Este motor de evaluación de reglas en JavaScript es muy simple y ligero. Su algoritmo de evaluación se basa en reglas de producción con una máquina de inferencia con  encadenamiento hacia adelante (forward chaining).

Entre las limitaciones de esta versión está el no incluir las reglas evaluadas dentro de la lógica de como se encontró una solución.

Otra limitación es que se ejecuta en el navegador. Una base de conocimientos muy extensa podría provocar lentitud en nuestro sitio Web debido a que dependería de la potencia de proceso del terminal que tengamos (no es lo mismo responder las preguntas en un móvil que responderlas en un iMac).

Código del proyecto

El código de este proyecto podemos descargarlo desde Google Code:


Diseño de un motor de reglas en JavaScript (Parte – III)


La base de conocimientos

La base de conocimientos escrita en JavaScript en notación JSON puede ser leída desde otro archivo .js, un servicio Web o una base de datos local o remota.

La base de conocimientos tiene 5 objetos de tipo Array obligatorios:

Objeto
Descripción
askfirst
La pregunta o preguntas iniciales a partir de las cuáles comienza las evaluaciones el motor de reglas
statefirst
Hechos que son establecidos como verdaderos a partir de los cuáles comienza la evaluación del motor de reglas
questions
Preguntas al usuario
facts
Todos los hechos, incluidos aquellos que son conclusivos
rules
Reglas que permiten obtener conclusiones relacionando hechos y respuestas a las preguntas


var knowledge = {
   
     // Pregunta o preguntas iniciales
    
     askfirst:[],
    
     // Hechos que inicialmente queremos establecer como    
     // verdaderos

     statefirst:[],
    
     // Definicion de las preguntas
    
     questions:[],
   
     // Hechos
  
     facts:[],

     // Reglas

     rules:[]
};

Vamos a escribir nuestra base de conocimientos con la información que ya tenemos sobre los coches.

Preguntas

La primera pregunta nos va permitir seleccionar el modelo de coche que vamos a comprar:

{qid:1,
      qtext:'Por favor, elija el modelo de coche que desea comprar',
      qdesc:'Cabrio: es modelo de coche descapotable de dos puertas.’+
      'Cupé: modelo de coche de dos puertas.' +
      'Berlina: modelo de coche de cuatro puertas.',
     qtype:'selection',
    info:[
      'cabrio',
      'cupe',
      'berlina']}


La segunda pregunta nos va a permitir identificar el rango de precios del coche que queremos comprar:

{qid:2,
      qtext:'Por favor, elija un rango de precios',
      qdesc:'Define el rango de precios del coche que queremos comprar',
     qtype:'selection',
    info:[
      '10,000-30,000€',
      '31,000-50,000€',
      '+50,000€']}

Las nueve preguntas siguientes equivalen a seleccionar uno de los coches de cada una de las nueve posibilidades que da la matriz de cruzar los tipos de coches con los rangos de precios:

Para los cabrios:

{qid:3,
      qtext:'Por favor, elija cabrio de entre 10,000 y 30,000€',
      qdesc:'',
     qtype:'selection',
    info:[
      'Peugeot 207',
      'Renault Megane',
      'Maxda MX5']}

{qid:4,
      qtext:'Por favor, elija cabrio de entre  31,000 y 50,000€',
      qdesc:'',
     qtype:'selection',
    info:[
      'BMW z4',
      'Volvo c70']}

{qid:5,
      qtext:'Por favor, elija cabrio de más de 50,000€',
      qdesc:'',
     qtype:'selection',
    info:[
      'Audi TTRS',
      'Audi TTS',
      'BMW Serie G']}


Para los cupes:

{qid:6,
      qtext:'Por favor, elija cupe de entre 10,000 y 30,000€',
      qdesc:'',
     qtype:'selection',
    info:[
      'Renault Laguna',
      'BMW Serie 1']}

{qid:7,
      qtext:'Por favor, elija cupe de entre  31,000 y 50,000€',
      qdesc:'',
     qtype:'selection',
    info:[
      'Audi A5',
      'Chevrolet Camaro',
      'Mercedes Clase E']}

{qid:8,
      qtext:'Por favor, elija cupe de más de 50,000€',
      qdesc:'',
     qtype:'selection',
    info:[
      'Audi TTS',
      'Chevrolet Corvette',
      'Porsche 911']}


Para las berlinas:

{qid:9,
      qtext:'Por favor, elija berlina de entre 10,000 y 30,000€',
      qdesc:'',
     qtype:'selection',
    info:[
      'Alfa Romeo MiTo',
      'Fiat Bravo']}

{qid:10,
      qtext:'Por favor, elija berlina de entre  31,000 y 50,000€',
      qdesc:'',
     qtype:'selection',
    info:[
      'Audi A5',
      'BMW Serie 5']}

{qid:11,
      qtext:'Por favor, elija berlina de más de 50,000€',
      qdesc:'',
     qtype:'selection',
    info:[
      'Audi S6',
      'BMW Serie 6',
      'BMW Serie 7']}


Hechos

Los hechos describen a cada uno de los coches con sus precios y otras características. Por simplicidad solo nos vamos a referir al precio.

{fid:1,ftext:'Peugeot 207 cabrio por 15,300€'}
{fid:2,ftext:'Renault Megane cabrio por 22,000€'}
{fid:3,ftext:'Maxda MX5 cabrio por 22,000€'}
{fid:4,ftext:'BMW z4 cabrio por 32,000€'}
{fid:5,ftext:'Volvo c70 cabrio por 37,000€'}
{fid:6,ftext:'Audi TTRS cabrio por 64,000€'}
{fid:7,ftext:'Audi TTS cabrio por 55,000€'}
{fid:8,ftext:'BMW Serie G cabrio por 83,000€'}
{fid:9,ftext:'Renault Laguna cupe por 23,000€'}
{fid:10,ftext:'BMW Serie 1 cupe por 24,000€'}
{fid:11,ftext:'Audi A5 cupe por 35,000€'}
{fid:12,ftext:'Chevrolet Camaro cupe por 44,000€'}
{fid:13,ftext:'Mercedes Clase E cupe por 44,000€'}
{fid:14,ftext:'Audi TTS cupe por 51,000€'}
{fid:15,ftext:'Chevrolet Corvette cupe por 86,000€'}
{fid:16,ftext:'Porsche 911 por 96,000€'}
{fid:17,ftext:'Alfa Romeo MiTo berlina por 14,000€'}
{fid:18,ftext:'Fiat Bravo berlina por 12,000€'}
{fid:19,ftext:'Audi A5 berlina por 34,000€'}
{fid:20,ftext:'BMW Serie 5 berlina por 35,000€'}
{fid:21,ftext:'Audi S6 berlina por 86,000€'}
{fid:22,ftext:'BMW Serie 6 berlina por 80,000€'}
{fid:23,ftext:'BMW Serie 7 berlina por 74,000€'}

Reglas

Las reglas combinan las preguntas y los hechos:

{rid:1,cond: 'q(1) == ”cabrio” && q(2)==”10,000-30,000€”',
       iftrue:'ask(3);',
       iffalse:''},

{rid:2,cond: 'q(1) == ”cabrio” && q(2)==”31,000-50,000€”',
       iftrue:'ask(4);',
       iffalse:''},

{rid:3,cond: 'q(1) == ”cabrio” && q(2)==”+50,000€”',
       iftrue:'ask(5);',
       iffalse:''},

{rid:4,cond: 'q(1) == ”cupe” && q(2)==”10,000-30,000€”',
       iftrue:'ask(6);',
       iffalse:''},

{rid:5,cond: 'q(1) == ”cupe” && q(2)==”31,000-50,000€”',
       iftrue:'ask(7);',
       iffalse:''},

{rid:6,cond: 'q(1) == ”cupe” && q(2)==”+50,000€”',
       iftrue:'ask(8);',
       iffalse:''},


{rid:7,cond: 'q(1) == ”berlina” && q(2)==”10,000-30,000€”',
       iftrue:'ask(9);',
       iffalse:''},

{rid:8,cond: 'q(1) == ”berlina” && q(2)==”31,000-50,000€”',
       iftrue:'ask(10);',
       iffalse:''},

{rid:9,cond: 'q(1) == ”berlina” && q(2)==”+50,000€”',
       iftrue:'ask(11);',
       iffalse:''},


{rid:10,cond: 'q(3) == ”Peugeot 207”',
       iftrue:'final(1);',
       iffalse:''},


{rid:11,cond: 'q(3) == ”Renault Megane”',
       iftrue:'final(2);',
       iffalse:''},

{rid:12,cond: 'q(3) == ”Maxda MX5”',
       iftrue:'final(3);',
       iffalse:''},


{rid:10,cond: 'q(4) == ”BMW z4”',
       iftrue:'final(4);',
       iffalse:''},


{rid:11,cond: 'q(4) == ”Volvo c70”',
       iftrue:'final(5);',
       iffalse:''},

{rid:12,cond: 'q(5) == ”Audi TTRS”',
       iftrue:'final(6);',
       iffalse:''},

{rid:13,cond: 'q(5) == ”Audi TTS”',
       iftrue:'final(7);',
       iffalse:''},

{rid:14,cond: 'q(5) == ”BMW Serie G”',
       iftrue:'final(8);',
       iffalse:''},


{rid:15,cond: 'q(6) == ”Renault Laguna”',
       iftrue:'final(9);',
       iffalse:''},

{rid:16,cond: 'q(6) == ”BMW Serie 1”',
       iftrue:'final(10);',
       iffalse:''},


{rid:17,cond: 'q(7) == ”Audi A5”',
       iftrue:'final(11);',
       iffalse:''},
{rid:18,cond: 'q(7) == ”Chevrolet Camaro”',
       iftrue:'final(12);',
       iffalse:''},
{rid:19,cond: 'q(7) == ”Mercedes Clase E”',
       iftrue:'final(13);',
       iffalse:''},

{rid:20,cond: 'q(8) == ”Audi TTS”',
       iftrue:'final(14);',
       iffalse:''},
{rid:21,cond: 'q(8) == ”Chevrolet Corvette”',
       iftrue:'final(15);',
       iffalse:''},
{rid:22,cond: 'q(8) == ”Porsche 911”',
       iftrue:'final(16);',
       iffalse:''},

{rid:23,cond: 'q(9) == ”Alfa Romeo MiTo”',
       iftrue:'final(17);',
       iffalse:''},
{rid:24,cond: 'q(9) == ”Fiat Bravo”',
       iftrue:'final(18);',
       iffalse:''},

{rid:25,cond: 'q(10) == ”Audi A5”',
       iftrue:'final(19);',
       iffalse:''},
{rid:26,cond: 'q(10) == ”BMW Serie 5”',
       iftrue:'final(20);',
       iffalse:''},

{rid:27,cond: 'q(11) == ”Audi A6”',
       iftrue:'final(21);',
       iffalse:''},
{rid:28,cond: 'q(11) == ”BMW Serie 6”',
       iftrue:'final(22);',
       iffalse:''},
{rid:29,cond: 'q(11) == ”BMW Serie 7”',
       iftrue:'final(23);',
       iffalse:''},


Preguntas iniciales

Las primeras preguntas a realizar son sobre el tipo de coche y el rango de precios, que son las preguntas 1 y 2.

askfirst:[1,2],


Hechos iniciales

Inicialmente no se establece ningún hecho:

statefirst:[],

Diseño de un motor de reglas en JavaScript (Parte – II)


Planteamiento del problema

Una empresa que vende automóviles por Internet, desea realizar un cuestionario que permita orientar a sus clientes sobre el tipo de coche que desea comprar.

Como es posible vender cientos de modelos de coches diferentes para diferentes usos, vamos a centrarnos en unas preguntas básicas:

  • El tipo de modelo de coche que se desea comprar (cabrio, cupé o berlina)
  • El rango de precio del coche que deseamos comprar

Con estas preguntas básicas podemos comenzar a crear nuestras reglas de negocio.


Información básica

Tipo
10,000-30,000€
31,000-50,000€
Más de 50,000€
Cabrio (2 puertas descapotable)
  • Peugeot 207(15,300€)
  • Renault Megane (22,000€)
  • Mazda MX5 (22,000€)
  • BMW Z4 (32,000€)
  • Volvo C70 (37,000€)
  • Audi TTRS (64,000€)
  • Audi TTS (55,000€)
  • BMW Serie G (83,000 €)
Cupé (2 puertas)
  • Renault Laguna (23,000€)
  • BMW Serie 1 (24,000€)
  • Audi A5 (35,000€)
  • Chevrolet Camaro (44,000€)
  • Mercedes Clase E (44,000€)
  • Audi TTS (51,000€)
  • Chevrolet Corvette (86,000€)
  • Porsche 911 (96,000€)
Berlina (4 puertas)
  • Alfa Romeo MiTo (14,000€)
  • Fiat Bravo (12,000€)
  •  
  • Audi A5 (34,000€)
  • BMW Serie 5 (35,000€)
  • Audi S6 (86,000€)
  • BMW Serie 6 (80,000€)
  • BMW Serie 7(74,000€)



Algoritmo del cuestionario básico


SI modelo_de_coche = “cabrio” ENTONCES
   SI rango_precios = “10,000-30,000” ENTONCES
       Elegir el coche
   SINO
       SI rango_precios = “31,000-50,000” ENTONCES
           Elegir el coche
       SINO
           Elegir el coche
       FIN SI
   FIN SI
SINO
    SI modelo_de_coche = “cupe” ENTONCES
        SI rango_precios = “10,000-30,000” ENTONCES
             Elegir el coche    
        SINO
            SI rango_precios = “31,000-50,000” ENTONCES
                Elegir el coche
            SINO
                Elegir el coche  
            FIN SI
        FIN SI
    SINO
       SI rango_precios = “10,000-30,000” ENTONCES
            Elegir el coche     
       SINO
           SI rango_precios = “31,000-50,000” ENTONCES
               Elegir el coche
           SINO
               Elegir el coche
           FIN SI
       FIN SI
    FIN SI
FIN SI

Este algoritmo es posible codificarlo en JavaScript directamente en un archivo .js que forme parte del sitio Web.

Esto crea una dependencia entre las reglas de negocio y el propio código y diseño del sitio Web. Si tenemos necesidad de cambiar una simple regla, como el rango de precios de los coches, tendremos que modificar parte del código del sitio para poder adaptarlo al cambio.

Un problema adicional es que no permite separar los roles a la hora de mantener las reglas de negocio. Con la existencia de un motor de evaluación de reglas y las reglas separadas, podemos encargar del mantenimiento del primero al personal de Tecnologías de la Información, mientras que las reglas pueden ser mantenidas, revisadas y actualizadas por el personal de ventas o de marketing.

El motor de reglas se encargaría de:

  • Hacer las preguntas adecuadas
  • Evaluar las diferentes variantes
  • Dar una selección
  • Aportar información de cómo el usuario llegó a esta selección

Este motor sería siempre el mismo para cualquier página Web o cualquier encuesta. Solo tendríamos que aportar como datos las preguntas, las conclusiones y las reglas para evaluarlas. Esta información podría recuperarse de un archivo .js, una base de datos, un servicio Web, etc.

El motor de reglas

El motor de reglas es una clase JavaScript que se encarga de evaluar un conjunto de respuestas para llegar a una determinada conclusión.

Las conclusiones a las que queremos llegar las vamos a determinar hechos (facts). Los hechos pueden ser establecidos como conclusión o no.

Las preguntas (questions) nos permiten responder:

  • Si una afirmación es correcta o no
  • Seleccionar un valor de un conjunto de valores discretos
  • Seleccionar un valor de un rango de valores numérico

Cada pregunta tiene además una parte descriptiva que nos aclara el sentido o el por qué hacemos la pregunta.

Por último están las reglas. Estas nos permiten evaluar hechos y respuestas de determinadas preguntas para llegar o establecer otros hechos como conclusiones.

Las reglas, los hechos y las preguntas conforman nuestra base de conocimientos.

Hechos

Establecen una determinada afirmación como verdadera. Poseen dos atributos:

Atributo
Descripción
FID
Fact Identification. Número entero que identifica a un hecho dentro de la base de conocimientos. Debe ser único.
FTEXT
Fact Text. Texto que describe al hecho

Ejemplos de hechos:

  • El coche es rojo
  • El coche es un Audi A8

Sintaxis del hecho en JavaScript:

{fid:1,ftext:'El coche es rojo'}
{fid:2,ftext:'El coche es un Audi A8'}


Preguntas

Las preguntas recaban la información que el motor de reglas necesita para establecer determinados hechos como conclusiones. Tenemos tres tipos de preguntas:

  • Booleanas. Preguntan si alguna información es verdadera  o falsa
  • Selección. Permiten elegir un valor de un grupo de valores
  • Rango: Permiten elegir un valor de un rango numérico de valores

Sus atributos son:

Atributo
Descripción
QID
Question Identification. Número entero que identifica a una pregunta dentro de la base de conocimientos. Debe ser único.
QTEXT
Question Text. Texto de la pregunta que deseamos hacer
QDESC
Question Description. Texto que permite aclarar o aportar más información sobre la pregunta que deseamos hacer. Esta información es opcional.
QTYPE
Question Type. Define el tipo de la pregunta: boolean, selection o range
INFO
Information. Aporta información adicional en el caso de preguntas de tipo selection o range. En las preguntas de tipo boolean este atributo siempre será null.


Sintaxis en JavaScript.

Pregunta de tipo booleana:

{qid:1,qtext:'Desea comprar un coche deportivo',
       qdesc:'',
       qtype:'boolean',info:null},   


Pregunta de tipo selection:

{qid:5,
      qtext:'Por favor, elija un rango de precios',
      qdesc:'Define el rango de precios del coche que queremos comprar’,
    qtype:'selection',
    info:[
      '10,000-30000€',
      ‘31000-50000€’,
      ‘+50,000€’]}


Pregunta de tipo range:

{qid:5,qtext: 'Cual es su evaluación del coche',
       qdesc:'Nota del coche entre 0 y 10',
       qtype:'range',info:[0,10]},   



Reglas

Las reglas relacionan determinados hechos y las preguntas para establecer otros hechos como conclusiones.

Básicamente, son estructuras de decisión simples tipo:

SI condición ENTONCES
     Acciones-1
SINO
     Acciones-2
FIN SI

Si se cumple la condición, entonces se ejecutan las acciones Acciones-1 y si no se ejecutan las Acciones-2.


Atributo
Descripción
RID
Rule Identification. Número entero que identifica a una regla dentro de la base de conocimientos. Debe ser único.
COND
Expresión lógica que relaciona hechos y respuestas a las preguntas
IFTRUE
Acciones a realizar en caso de que se cumpla la condición
IFFALSE
Acciones a realizar en caso de que no se cumpla la condición

Sintaxis en JavaScript:

{rid:1,cond: 'q(1)',
       iftrue:'ask(3);set(3);',
       iffalse:'ask(2);'},

Esto se interpreta como:

Si  la respuesta a la pregunta 1 es verdadera:

  • Hacer la pregunta 3
  • Establecer el hecho 3 como verdadero

Si la respuesta la pregunta 1 es falsa:

  • Hacer la pregunta 2

Condiciones

Las condiciones son expresiones booleanas (devuelven verdadero o falso) formadas por los siguientes elementos:

  • Respuestas a preguntas [función q()]
  • Evaluar si un hecho es verdadero o no [función f()]
  • Operadores de comparación
  • Operadores lógicos

Función q(qid)

Función que devuelve el valor de la pregunta identificada en la base de conocimientos como qid.

El valor devuelto depende del tipo de la pregunta:

  • Boolean. Devuelve true o false
  • Selection. Depende una cadena de caracteres
  • Range. Devuelve un número entero entre el rango de valores especificado.



Función f(fid)

Función que devuelve verdadero o falso en dependencia de si se ha establecido o no un hecho identificado con el parámetro fid.


Operadores de comparación

Se utilizan los mismos operadores de JavaScript teniendo en cuenta que se comparan valores booleanos, enteros o de cadenas de caracteres.

Operador
Descripción
Ejemplo
==
Igual en valor
q(2) == “rojo”
===
Igual en valor y tipo
q(3) === 10
!=
Desigual en valor
q(2) != “rojo”
!==
Desigual en valor y tipo
q(3) !== 10
> 
Mayor que
q(3) > 40
>=
Mayor o igual que
q(3) >= 40
< 
Menor que
q(3) < 40
<=
Menor o igual que
q(3) <= 40

Operadores lógicos y de agrupación

Se utiliza el paréntesis como operador de agrupación y además los operadores lógicos que permiten relacionar expresiones booleanas:

Operador
Descripción
Ejemplo
!
NOT o negación lógica
!(q(2) == “rojo”)
&&
AND o “Y” lógico
q(3) && f(2) && q(1)
||
OR u “O” lógico
q(2) || q(3)


Acciones

Las acciones están orientadas a:

  • Establecer determinados hechos como verdaderos [función set()]
  • Realizar determinadas preguntas [función ask()]
  • Establecer determinados hechos como conclusión final [función final()]

La función set(fid)

Esta función permite establecer un hecho como verdadero aunque no se alcance ninguna conclusión. El parámetro es el número que identifica el hecho.

La función ask(qid)

Esta función permite que el motor de reglas realice al usuario la pregunta identificada por qid.

La función final(fid)

Esta función permite establecer un hecho como verdadero estableciendo una conclusión. El parámetro es el número que identifica el hecho. Cuando el motor de reglas establece una conclusión, detiene la evaluación de reglas.


Entradas antiguas Inicio