6.21.2.模型、范围及命名规则
模型为模板(template)和规则(rule)定义了一个命名空间(namespace)。意味着两个不同的模型可以各自包含一个同名的规则而不会产生冲突——特别是名为MAIN::initialize和COMMUTE::initialize的规则可以同时定义并存在于同一程序中。类似的在两个不同模型中的可以定义两个同名的template,如:COMPUTER::bus和COMMUTE::bus。问题是Jess如何区分规则定义或查询(query)所指的是哪个template。
当Jess编译一条规则或deffact定义时,将依次从以下三个地方查找模板:
1.当pattern明确的命名了一个模型时,仅在该模型中查找。
2.当pattern没有明确命名时,则先在包含该规则的定义的模型中查找。
3.如果仍没找到,则最后在MAIN模块中查找。注意:MAIN模型类似一种template的全局命名空间(namespace)。
下例描述了以上三种可能性:
Jess> (assert (MAIN::mortgage-payment 2000)) <Fact-0> Jess> (defmodule WORK) TRUE Jess> (deftemplate job (slot salary)) TRUE Jess> (defmodule HOME) TRUE Jess> (deftemplate hobby (slot name) (slot income)) TRUE Jess> (defrule WORK::quit-job (job (salary ?s)) (HOME::hobby (income ?i&:(> ?i (/ ?s 2)))) (mortgage-payment ?m&:(< ?m ?i)) => (call-boss) (quit-job)) TRUE Jess> (ppdefrule WORK::quit-job) "(defrule WORK::quit-job (job (salary ?s)) (HOME::hobby (income ?i&:(> ?i (/ ?s 2)))) (MAIN::mortgage-payment ?m&:(< ?m ?i)) => (call-boss) (quit-job))"
上例中,在三个不同的模型中定义了三个deftemplate,分别是:
MAIN::mortgage-payment, WORK::job,和HOME::hobby。
Jess通过在WORK模型中的定义找到WORK::job template;通过在调用时所明确指定的模型名找到HOME::hobby template;通过最后对MAIN模型的搜索找到MAIN::mortgage-payment template。
接受结构名作为参数的命令(如:ppdefrule, ppdeffacts等)将以与上述同样的方式搜索结构名。
注意:许多列举结构的命令(如:facts, listdeftemplates, rules等)允许使用模型名称或“*”作为可选参数。如果没有指定参数,这些命令仅作用于当前的模型上。如果有指定的模型名称,则该命令作用该指定模型上。如果指定模型为“*”,则作用在所有模型上。
6.21.3模型聚焦(module focus即处于选中状态)及执行控制
之前描述了如何使用模型提供的命名空间(namespace)的功能将许多规则(rulebase)分割为容易管理的块。除此之外模型还可以用于控制执行。通常,虽然Jess中所有的规则都可以被即时触发,但是只有处于被选中状态的模型(focus module)中的规则会执行。注意focus module和之前讨论过的当前模型(current module)是两个不同的概念。
最初处于选中状态的是MAIN模型:
Jess> (defmodule DRIVING) TRUE Jess> (defrule get-in-car => (printout t "Ready to go!" crlf)) TRUE Jess> (reset) TRUE Jess> (run) 0
在上例中规则并没有执行,因为DRIVING模型没有被选中。可以通过focus函数改变当前选中的模型(该函数返回前一个处于选中状态的模型的名称:)
Jess> (focus DRIVING) MAIN Jess> (run) Ready to go! 1
注意:可以在一个规则的RHS调用focus,即可在引擎运行时改变当前选中的module。
事实上Jess保存一个focus栈,可以记录任意多个模型。从定义上说focus module就是位于栈顶的模型。当focus module中没有被激活的规则时,该模型就被pop出栈,而其后的module就成为当前focus module。可以通过使用pop focus, list-focus-stack, get-focus-stack及clear-focus-stack函数复制focus堆栈。
例dilemma.clp就对模型的控制执行作了很好的描述。
6.21.3.1 auto-focus的声明
可以声明一个带有auto-focus属性的rule:
Jess> (defmodule PROBLEMS) TRUE Jess> (defrule crash (declare (auto-focus TRUE)) (DRIVING::me ?location) (DRIVING::other-car ?location) => (printout t "Crash!" crlf) (halt)) TRUE Jess> (defrule DRIVING::travel ?me <- (me ?location) => (printout t ".") (retract ?me) (assert (me (+ ?location 1)))) TRUE Jess> (assert (me 1)) <Fact-1> Jess> (assert (other-car 4)) <Fact-2> Jess> (focus DRIVING) MAIN Jess> (run) ...Crash! 4
当一个带有auto-focus属性的rule被激活时,其所在的模型自动被压入focus堆栈中,并成为focus module。带有auto-focus rule的模型“后台任务(background tasks)”很强。
6.21.3.2 从规则的RHS返回
如果在一个规则的RHS调用return函数,则会立即停止该规则RHS的执行。而且,当前的focus模型从focus堆栈中出栈。
由此可以像调用子程序一样调用模型。可以在一个规则的RHS用focus函数调用模型,并且使用return函数返回值。