深入探究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的内容和结构。
在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="Select problem">
</form>
knowledgebase正是“变量”select的名称,而animal则是该变量的一个值
该变量的取值,有3种可能:animal,Starting,Tyre
该变量的取值,是由用户在浏览器决定的