首页 > 代码库 > Visual Prolog 的 Web 专家系统 (3)

Visual Prolog 的 Web 专家系统 (3)

深入探究WEB GENI源程序

折腾WEB GENI的目的,是为了摸清它的结构设计、运行机制,为山寨出自己的专家系统外壳,积累经验,启发思路。

为此,必须深入细致地研读源程序代码。

WEBGENI包括2个模块:geni.pro和cgitools.pro。前者是主程序,负责推理、生成网页等;后者负责相对底层的CGI数据处理。本文探究的对象,主要是geni.pro。

Visual Prolog(以下简称VIP)程序由几种代码段构成:

PREDICATES :谓词段。相当于C语言的函数声明

CLAUSES :子句段。相当于C语言的函数实现

DOMAINS :域段。相当于C语言声明数据类型结构

DATABASE 或 FACTS : 内部数据库(事实)段。它是Prolog特有的机制,实际上是保存结构化数据的内存缓冲。Erlang Eresye有对它的模仿。

GOAL :目标段。程序运行的起点,相当于C语言的函数main()。

程序的起点GOAL段

GOAL
	startpage,
	CGI_String = cgi_GetString(),		
	str_namelist(CGI_String,ParmList),
	consult_kb(ParmList,ParmList1),
	userdefined_startpage(),
	write_startform(),
	assert_conditions(ParmList1),
        infer().

以下,逐句追踪、分析程序。

1、子句startpage

  startpage :-
        write("Content-type: text/html\n\n"),
        write("<HTML>\n"),
        write("<HEAD>\n"),
        write("<TITLE>\n"),
        write("Prolog Development Center A/S EXPERT SYSTEM\n"),
        write("</TITLE>\n"),
        write("</HEAD>\n"), 
        write("<BODY bgcolor=yellow>\n").

本程序在服务器端运行,对客户端浏览器的请求,做出以上回应。

2、子句cgi_GetString()

它的谓词声明在文件cgitools.pre中。.pre文件,专门用来声明谓词。

GLOBAL PREDICATES
  procedure STRING cgi_GetString()

Global 修饰的谓词、常量、域等,允许多个程序模块调用。

关键字procedure把谓词定义为子程序。子程序从不失败(fail),从不回溯,只有一个运行结果(但运行时错误除外)。

在文件cgitools.pro中,定义子句 cgi_GetString( CGI_String )

注意!

谓词cgi_GetString()声明时没有参数,而子句cgi_GetString( CGI_String )实现时有参数。
声明与定义的这种不一致,VIP编译器并不认为有错。
而且,CGI_String = cgi_GetString() 这样的赋值语句,也不是Prolog应该有的。
为什么会这样,不知道。

VIP还有些类似的情况,下面还会遇到。但愿VIP没错,仅仅是我没弄明白。

3、关注CGI数据的内容

我用了很长时间在cgitools.pro中察看cgi_GetString()获取CGI_String,和str_namelist获得ParmList的过程。
我觉得,这两件事情的细节,与建造专家系统关系不大,没必要研究。
重要的是,弄清楚CGI_String与ParmList的内容和结构。

从cgi_GetString的谓词声明可见,CGI_String是字符串。

在cgitools.dom中,有以下声明:

GLOBAL DOMAINS
  PARM = parm(STRING Name,STRING Val)
  PARMLIST = PARM*

可见,ParmList的结构是这种形式:
[parm("aaa","one"),parm("bbb","two"),parm("ccc","three")]

可用以下办法验证。
把geni.pro中的GOAL段全部用注释屏蔽,用以下代码取代:

PREDICATES
write_vars(PARMLIST)
write_data(STRING)
CLAUSES
    write_data(CGI_String):-
    	write("<p>"),
    	writef("%",CGI_String),
    	write("</p>").

    write_vars([]):-!.
    write_vars([parm(Key,Value)|Rest]):-
        writef("<tr><td>%</td><td>%</td></tr>\n",Key,Value),
        write_vars(Rest).
GOAL
	startpage,
	CGI_String = cgi_GetString(),
	file_str("c:\\dd.dat",CGI_String),
	str_namelist(CGI_String,ParmList),
	write_data(CGI_String),
	write("<table>\n"),
	write_vars(ParmList),
	write("</table>\n"),
        write("<br />\n"),
        write("</body></html>\n").

谓词file_str,很有Prolog特色。
file_str (STRING OSFileName, STRING StringVariable)
流模式(i, o), (i, i)
读写文件,用同一个“函数”,怕是只有Prolog做得到。

本例,把字符串存入文件dd.dat。
文件内容是:
knowledgebase=animal

两个谓词write_vars(PARMLIST)和write_data(STRING),写入网页的内容,可自行验证。

“knowledgebase=animal”的来源是这样的:
打开文件 D:\Apache2.2\htdocs\GENI\default.htm

<form action="geni.exe" method="post">
<select name="knowledgebase" size="1">
<option>animal</option>
<option>Starting</option>>
<option>Tyre</option>>
</select>
<input type="submit" value=http://www.mamicode.com/"Select problem">
</form>

knowledgebase正是“变量”select的名称,而animal则是该变量的一个值
该变量的取值,有3种可能:animal,Starting,Tyre
该变量的取值,是由用户在浏览器决定的