知识工程T-PROLOG(5)

2019-03-16 15:06

这是一个含递归的程序,如果我们不断地问:?-is-integer(N). /* 当PROLOG回答后用户又打* /,则我们将以递增次序得到所有可能的整数:0,1,2,?,一次一个。实际上每次打“;”就迫便PROLOG回溯一次,表示不满足于上次给出的解。这与例1中的目标不成功的回溯含义略有一点不同。

下面让我们仔细地看一个PROLOG的搜索过程。

①一开始目标is-integer(N)匹配第1行事实,即指针P指向事实1,N被实例化为0,所以PROLOG回答N=0

②当打“;”后,PROLOG企图再次满足目标is-integer(N),于是从P所指向的事实1向下继续搜索,此后,目标is-integer(N)匹配第2行规则头,指向第2行,N与Y共享。

因为第2行是规则,所以必须设法满足该规则体中的两个子目标 is-integer(Y)与X is Y+1.

首先满足子目标is-integer(Y)。作为一个新目标,PROLOG又从数据库的顶开始扫描,找到第1行事实,即子目标is-integer(Y)匹配第1行其实,指针P1指向它,且Y实例化为0。因为是递归,为了区分不同的目标扫描数据库,我们把规则中的变量标以编号。

接着去满足P指向的第二个子目标X is Y+1,此目标成功,且X=Y+1=1,这时整个规则匹配成功,因此目标is-integer(N)也成功,有N=X=1,于是PROLOG回答N=1。

③当继续打“;”,这就意味着希望再找另一个整数,于是迫使PROLOG回溯再次去企图满足目标is-integer(N).这时目标is-integer(N)的指针P指向第二个规则,因为该规则包含了两个目标,因此要使目标is-integer(Y),X is Y+1的重新满足,同上述一样,此时Y将实例化为1,于是有:

X=Y+1=1+1=2

即目标is-integer(N)成功,且N=X=2,因此PROLOG回答: N=2

④同理,再打“;”,PROLOG将回答N=3,读者可模拟一下,看PROLOG是如何继续搜索的。

PROLOG在满足上面目标的搜索过程中,对事实和规则的选择,可用树结构清楚地表现。(如图6-15所示)。

图6-15

如果利用事实1来匹配(解)目标is-integer(N),则不产生更多的目标,如果利用规则2匹配,则另一个子目标is-integer(Y)被产生,于是对此必须做相同的选择。

3.截断(Cut)

在介绍cut之前,先归纳一个目标成功和失败的情况,一个目标成功有三种情况:

①与一个事实匹配。

②与一个规则的头匹配,且该规则体的所有子目标成功。 ③系统的内部谓词执行成功。 一个目标的失败,有四种情况: ①无子句可匹配。

②与一个规则的头匹配,但体执行失败。 ③系统的内部谓词执行失败。

④一度执行成功,而后在执行中发生回溯,又没有别的选择分枝。 Cut是一种能影响PROLOG回溯方式的专门机构,用户可以利用cut来告诉PROLOG,当要回溯前面一串已满足的目标时,就不必再考虑那些目标的其它可选择枝。因为用户事先知道这种不必要的回溯对求解毫无意义。故在程序中合理使用cut,具有两个优点:

事实3(回

答N=2)

其它

事实1(回答N=0)

规则2

1

事实2(回答N=1)

规则3

①程序可以运行得更快,因为它将不会浪费时间去企图满足那些能事先知道不可能导致解的目标。

②程序可以占有较少的计算机存贮空间,因为PROLOG不必为它以后的检查去记录那些对解无帮助的回溯点信息。

从语法上讲,在一规则中利用cut就好象是用一个目标,它的谓词是!但没有变元。作为一个目标它将立即成功,但不能被重新满足,其副作用是改变这以后的回溯工作方式,所谓不能被重新满足,是指当回溯到达到一目标时,不可能再找另一个满足该目标的成功解。换句话说,企图再满足它时引起立即失败,在给出具体例子说明cut的使用之前,让我们先从直观的and-or树中来解释cut的作用。现假设有一棵and-or树,见图6-16。设在匹配子句1时,子目标gl匹配cl成功,子目标g2匹配子句c4成功,cut也成功,但在满足子目标g4时失败,按照PROLOG的正常搜索方法,当子目标g4失败后,将引起回溯,若不存在cut,则首先要求子目标g2寻找另一个可能匹配成功的子句C5。如果g2失败,PROLOG将回溯到子目标gl,对子句C2,C3进行必要的匹配试验。现在因为有了cut,它不可重新被满足且立即失败,因此改变了回溯工作方式,PROLOG将不对C2,C3,C5作进一步匹配试验。这就好象把虚线部分的枝条都已剪除。由此产生从父目标(匹配子句1)到子目标g2之间的所有目标都不可重新满足。在一些PROLOG系统中用符号>未代替!因符号>更能形象地表示搜索只能从左到右通行,而不允许从右到左通行(即回溯)。

下面通过一些简单的例子,未说明cut的三种使用方法。 (1)放在产生器和测试器的后面

Cut的第一种用法是把!放在产生器的后面,典型的模式是:

example(X):-generate(X),test(X),!.

产生器generate(x)不断地产生一些值,每产生一个值,测试器立即测试该值是否满足条件,若不是则回溯到产生器,由产生器产生下一个值再测试,直至找到一个满足条件的解,此时执行到达cut,它指明已找到唯一需要的解,没有必要或不可能再找其它解。

c1 c3 c3 c4 c5 g1 g2 ! g4 子句1 子句2 目标

图6-16

例3,利用加法和乘法未写整数除程序。 divide(N1,N2,Result):-is-integer(Result),

P1 is Result * N2, P2 is (Result-1)*N2, P1=N1,!.

这条规则利用前面的谓语is-integer来不断产生整数,直到产生的数是N2的结果,所以is-integer用作产生器,而剩余的目标起着测试器的作用。由于最后用了cut,因此对给定的值N1和N2,divide(N1,N2,Result)只能对一个可能的值成功。虽然is-integer能够无限地产生许多侯选的数,不过一旦成功地找到结果,cut就终结了任何可能的回溯,若问

?—divide(32,5,X).

X=6; No

因为6*5≤7*5,这里cut的作用是把父目标divide(32,5,X)到cut之间的所有目标(包括is-integer(Result),Pl, is Result*N2>N1)指针都去掉,使得它

们不能被重新满足,因此这种cut说明已经找到唯一正确的解,于是立即停止找另一解的任何企图,或者说终结一个产生和测试序列。

(2)与fail的组合

cut的第二种用法是常与另一个内部谓词fail组合使用,fail象内部谓词cut一样没有变元。Fail作为一个目标总是失败并引起加溯。当fail的前面是cut时,则由于cut的作用将改变正常的回溯方式,使匹配含该cut规则的父目标立该失败(因从父目标到cut之间的所有目标均不可重新满足)。cut和fail的组合使用在实际应用中是很有用的。

下面讨论如何在一程序中利用这种组合来描述一个人是健壮的。

例4,我们要描述一个人是健壮的,条件是,如果他没有心脏病,没有肺病,不是近视眼等等。

也许我们开始会写出规则: strong(X): -heart-disease(X),fail. strong(X): -tuberoulosis(X),fail. strong(X): -bearsight(X),fail. strong(X).

第1条规则企图说,如果X是有心脏病的人,则匹配它的目标将失败,同样,第2,3条规则说的是,若X有肺病或近视眼,则他不是强壮的。而第4行的子句则企图说如果X没有诸疾病,则他是强壮的。但上述程序是不正确的,假若问:

?--strong(wan-lin)

用假设已存在一事实heart- disease(wan-lin), 则我们本来希望询问中目标匹配第一条规则后得到回答no,但由于匹配第1条规则时,目标heart—disease(X)成功后接着遇到目标fail,它总是失败,于是引起回溯。这样造成PROLOG的因溯功能引起的结果。为了停止PROLOG这种不必要的回溯,可以用cut放在fail之前完成。于是改写为:

strong(X):-heart—disease(X),!,fail. strong(X):-tuberoulosis(X),!,fail. strong(X):-nearsight(X),!,fail. strong(X).


知识工程T-PROLOG(5).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:职务型经济犯罪疑难问题对话录

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: