2004.11.3 AI程序设计 1
第 二 部分:第 6章 类与对象
第 6章 类与对象
本章主要介绍在 Visual Prolog 6中的面向对象的
概念, 并将列举一些实例, 以使读者尽快熟悉这个概念 。
主要内容包括对象模型, 类实体, 模块, 创建和访问对
象, 接口对象类型, 多重实现, 包容多态性, Support
类型扩展, Object超类型, 继承以及 Visual Prolog 5
与 Visual Prolog 6的差别等 。
2004.11.3 AI程序设计 2
第 二 部分:第 6章 类与对象
第 6章 类与对象
6.1 对象模型
6.2 类实体
6.3 模块
6.4 创建和访问对象
6.5 接口对象类型
6.6 多重实现
6.7 包容多态性
6.8 support类型扩展
6.9 object超类型
6.10 继承
6.11 对象体系的其他特点
6.12 Visual Prolog 5与 Visual Prolog 6的差异
本章小结与习题
2004.11.3 AI程序设计 3
第 二 部分:第 6章 类与对象
6.1 对象模型
? 在 Vip6中,对象模型的语义实体是对象、对象类型和类。有关这些
实体的概念是接口、类的声明及实现。接口是一组命名的谓词声明。
接口描述了对象之间的“界面”,并因此而得名,即它是从一个对
象之外进入到对象内部的入口。接口描述了对象类型。
? 考虑这个接口定义,
interface person
predicates
getName, () -> string Name,
setName, (string Name),
end interface person
? 这是一个名为 "person"的接口的定义。在这里,所有 "person"类的
对象都有两个谓词 "getName"和 "setName",其声明如上所示。
2004.11.3 AI程序设计 4
第 二 部分:第 6章 类与对象
6.1 对象模型
? 接口只定义对象的类型;而对象由类产生。一个类包含类的声明和
类的实现。一个创建 person对象的类可以这样声明,
class person_class, person
constructors
new, (string Name),
end class person_class
? 这是一个名为 person_class 的类的声明,可以由 person_class类
构造 person类型的对象。这个类有一个名为 new的构造函数,给
new一个 Name就能创建一个对象(属于 person类型)。
2004.11.3 AI程序设计 5
第 二 部分:第 6章 类与对象
6.1 对象模型
? 这个类还需要一个实现,如下面的代码所示,
implement person_class
facts
name, string,
clauses
new(Name),- name,= Name,
clauses
getName() = name,
clauses
setName(Name),- name,= Name,
end implement person_class
2004.11.3 AI程序设计 6
第 二 部分:第 6章 类与对象
6.1 对象模型
? 这是名为 person_class的类实现。这个实现必须为诸如 new、
getName和 setName的每个公共谓词和构造函数提供定义。这个
实现也可以局部声明和定义附加实体,这种实体仅在这个实现中可
见。在这个例子中,这个类声明了一个名为 name的事实变量以存储
人的名字。
? 对于事实变量 name,每个对象都有自己的实例。上面子句中的代码
都能引用具有事实变量的那个特例。我们把这种谓词称为对象谓词,
把这个事实变量称为对象事实。
2004.11.3 AI程序设计 7
第 二 部分:第 6章 类与对象
6.2 类实体
? 一个类也可以有为这个类的所有对象共享的实体。我们举个例子,
将上面的例子扩展,添加代码来为 person_class类创建的对象个数
计数。这个数字会随着每个对象的创建而增加,且永不减少。
? 我们用一个谓词,即谓词 getCreateCount,将类声明予以扩展,这
个谓词用于返回当前计数值,
class person_class, person
constructors
new, (string Name),
predicates
getCreatedCount, () -> unsigned Count,
end class person_class
2004.11.3 AI程序设计 8
第 二 部分:第 6章 类与对象
6.2 类实体
? 注意,公共可访问类谓词在类声明中进行声明,而公共可访问对象
谓词在接口中声明。这个规则没有例外:不可能在类声明中声明对
象谓词,也不可能在一个接口中声明类谓词。
? 这个谓词需要在类实现中定义。此外,还需要一个事实来存储计数
值。这个事实必须是一个类事实,即它为所有对象所共享。在一个
类的实现里,可以声明并定义私有对象实体以及私有类实体。声明
类实体时,在相关的声明段前加关键词 class。
2004.11.3 AI程序设计 9
第 二 部分:第 6章 类与对象
6.2 类实体
? 注意,我们的 person_class类的实现如下,
implement person_class
class facts
createdCount, unsigned,= 0,
clauses
getCreatedCount() = createdCount,
facts
name, string,
clauses
new(Name),-
name,= Name,
createdCount,= createdCount+1,
clauses
getName() = name,
clauses
setName(Name),- name,= Name,
end implement person_class
?在本例中, 我们添加了一个类
事实 createCount,并将其初始
化为零 。 并且为谓词
getCreateCount添加了一个子
句, 用于返回事实
createdCount的当前值 。 最后,
我们在构造函数中添加代码, 使
变量 createdCount递增 。
?注意,在构造函数中,有两处
赋值形式相同,但一个是更新对
象状态的,另一个是更新类的状
态的。
2004.11.3 AI程序设计 10
第 二 部分:第 6章 类与对象
6.3 模块
? 类的一种特殊变体根本不能产生对象,所以就它们所起的作用来讲,
应称之为“模块”而不是类。
class io % no type here
predicates
write, (string ToWrite),
write, (unsigned ToWrite),
end class io
? 这样一个不能创建对象的类很明显是不能包含对象实体的,也不可
能有构造函数。
2004.11.3 AI程序设计 11
第 二 部分:第 6章 类与对象
6.4 创建和访问对象
? 基于上述代码, 可以创建一个能创建对象且用 io类来写人名的目标 goal
( 在此对 io的类实现暂时不做考虑 ) 。
goal
P = person_class::new("John"),
Name = P:getName(),
io::write(Name),
? 第一行,调用 person_class类构造函数 new。创建的对象绑定到变量 P。
第二行,引用了 P中对象谓词 getName,并将结果绑定到变量 Name。最
后一行调用了 io类的类谓词 write。
? 注意,引用类中的名字时要用双冒号,如,person_class::new。同样,
引用对象谓词时要用单冒号,如,P:getName。
? 最后还应注意,尽管构造函数并不像一般函数那样声明,但它们是返回对象
的函数:返回类型包含在类声明中。
2004.11.3 AI程序设计 12
第 二 部分:第 6章 类与对象
6.5 接口对象类型
? 前面已经提到,接口是对象类型。按照字面意义来讲,在用到非对
象类型的地方是可以使用接口的。例如,在如下谓词声明中,
class mail
predicates
sendMessage, (person Recipient,string Message),
end class mail
? 谓词 mail::sendMessage以 person(接口)和 string作为参数。
2004.11.3 AI程序设计 13
第 二 部分:第 6章 类与对象
6.6 多重实现
? 我们可以创建多个完全不同的类,这些类都创建 person对象。我们
只需声明和实现更多的可构造 person对象的类。这些类的实现可以
有很大区别,比如,可以创建一个将 person存放在数据库中的类。
以下便是这样一个类的声明,
class personInDB_class, person
constructors
new, (string DatabaseName,string Name),
end class personInDB_class
? 这里不关心具体的实现。下面的代码显示的是一个关于某特定对象
类型的对象,但它可以有一个完全不同的实现。
2004.11.3 AI程序设计 14
第 二 部分:第 6章 类与对象
6.6 多重实现
implement personInDB_class
facts
db, database,
personID, unsigned,
clauses
new(DatabaseName,Name),-
db,= database_class::getDB(DatabaseName),
personID,= db:storePerson(Name),
clauses
getName() = db:getPersonName(personID),
clauses
setName(Name),- db:setPersonName(personID,Name),
end implement personInDB_class
? 值得注意的是,从这里可以看到,不仅内部行为完全不同,在内部状态上,
结构和内容也全然不同。
2004.11.3 AI程序设计 15
第 二 部分:第 6章 类与对象
6.7 包容多态性
? 无论无论同一种类型的对象的实现有多大区别, 它们都可以用在同一个场合
里 。 例如, 我们可以用上面所定义的 mail类向一个人 ( 对象 ) 发送消息,
不管那个人是由 person_class还是 personInDB_class构造的 。
goal
P1 = person_class::new("John"),
mail::sendMessage(P1,"Hi John,..."),
P2 = personInDB_class::new("Paul"),
mail::sendMessage(P2,"Hi Paul,..."),
? 这种行为称为包容( subsumption):只要两个对象都是那段上下文需要
的类型,那么由这个类构造的对象或由那个类构造的对象一样可用。
? 还可以看到, 谓词 mail::sendMessage可以用于任意 person类的对象中,
所以从某种意义上来讲, 即从包容多态 ( Subsumption Polymorphism)
意义上来讲, 这个谓词是多态的 ( polymorphic) 。
2004.11.3 AI程序设计 16
第 二 部分:第 6章 类与对象
6.8 support类型扩展
? 在我们的程序中会涉及到一种特殊的人,即程序的用户。用户有名字,还有
一个密码。我们想为用户创建一个新的对象类型或接口,以规定用户是一个
人,且有一个密码。为此,我们使用支持限定符( support qualification)
interface user supports person
predicates
trySetPassword, (string Old,string New,string Confirm) determ,
validatePassword, (string Password) determ,
end interface user
? 上面代码中,规定了 user支持 person。这有两个作用,
1) 它意味着 user对象必须提供在 person接口中所声明的谓词 ( 如谓词
getName和 setName) 。
2) user类型的对象同时也是 person型的对象,因此也能用于期望使用 person
对象的上下文中。
2004.11.3 AI程序设计 17
第 二 部分:第 6章 类与对象
6.8 support类型扩展
? 也就是说,假设有一个 user类,
class user_class, user
constructors
new, (string Name,string Password),
end class user_class
? 那么,这个类的对象就可以被 mail::sendMessage使用,
goal
P = user_class::new("Benny","MyCatBobby"),
mail::sendMessage(P,"Hi Benny,..."),
2004.11.3 AI程序设计 18
第 二 部分:第 6章 类与对象
6.8 support类型扩展
? 一个接口可以支持多个其他的接口,也就是说,
? 该种类型的对象必须提供在被支持的接口中的全部谓词;
? 该种类型的对象同样具有所有其他的类型。
? 一个接口同样可以支持一个或多个接口,而这些接口本身也能支持
一个或多个接口,如此等等。同样在这个例子中,
? 该种类型的对象必须提供被间接及直接支持的接口中的所有谓词
? 该种类型的对象具有所有其他的间接类型及直接类型
? 这种支持限定产生了子类型层次:我们称 user为 person的一个子类
型。
2004.11.3 AI程序设计 19
第 二 部分:第 6章 类与对象
6.9 object超类型
? 一个接口并不明确地支持其它任何接口,但却隐含地支持 object接
口。 object是一种隐含定义的、没有内容(即没有谓词)的接口。
任何对象都直接或间接地支持 object接口,所以任何对象都含有
object类型。因此,我们称 object为所有对象类型的超类型。
2004.11.3 AI程序设计 20
第 二 部分:第 6章 类与对象
6.10 继承
? 当对 user_class类进行类
实现时, 我们当然可以利
用 person类中的代码来减
少工作量 。 假设 user类与
person_class类很相似,
不 同 之 处 仅 在 于
user_class类还涉及密码 。
我们想要 user_class继承
person_class 类中
person的部分实现 。 这可
以用下面的代码完成,
implement user_class
inherits
person_class
facts
password, string,
clauses
new(Name,Password),-
person_class::new(Name),
password,= Password,
clauses
trySetPassword(Old,New,Confirm),-
validatePassword(Old),
New = Confirm,
password,= New,
clauses
validatePassword(Password),-
password = Password,
end implement user_class
2004.11.3 AI程序设计 21
第 二 部分:第 6章 类与对象
6.10 继承
? 这个实现表明它继承了 person_class,这样会有如下作用,
? 一个 person_class类的对象被植入到每一个构造的 user_class对象中 。
? 接口 person中的所有谓词可直接从 person_class继承到 user_class中 。
? 当继承一个谓词时, 不再需要直接描述它的实现, 而是使用所继承类
中的谓词实现 。
? 在某种意义上, 继承可以看作是语法上的修饰 。 至少我们还可以通过
下面的代码达到相同的效果 ( 有关密码谓词的子句同上 ),
2004.11.3 AI程序设计 22
第 二 部分:第 6章 类与对象
6.10 继承
? 在这段代码里, 没有继承
person_class类, 而是创建了一
个 person_class类的对象并将其
存储到一个事实变量里 。 这里没有
继承 getName和 setName的代码,
而是再次对这两个谓词做了一些琐
碎的实现, 它们直接将相应的任务
委托给事实变量中的对象 。
implement user_class
facts
person, person,
password, string,
clauses
new(Name,Password),-
person,= person_class::new(Name),
password,= Password,
clauses
getName() = person:getName(),
clauses
setName(Name),- person:setName(Name),
,.,
end implement user_class
2004.11.3 AI程序设计 23
第 二 部分:第 6章 类与对象
6.10 继承
? 这一段代码与前面的代码有着几乎相同的作用, 但是毕竟还有一
些显著的区别:首先, 需要写更多的代码 。 其次, person_class
类没有被植入 user_class类之中, 相反, 有一个对它的引用 。 而
且, 在此涉及到两次内存分配, 而不是一次 。 最后, 可以动态地
将事实变量的值改变为另一个对象, 这只需给事实变量指派一个
新的对象即可实现 。 例如, 将其改变成 personInDB_class类的
一个对象 。
? 应当注意, 第二个实现里有一个间接的调用 。 Visual Prolog处理
这种间接调用时很有效, 但在处理继承时更加高效 。
? Visual Prolog 6支持多重继承, 即可以同时从多个类继承 。
2004.11.3 AI程序设计 24
第 二 部分:第 6章 类与对象
6.11 对象体系的其他特点
? 以上介绍了 Vip6的对象体系里的最为基础的概念 。 在对象体系中
还有别的一些有趣的特点, 在此不做介绍, 例如,
– 对象支持实现中的更多的接口。
– 存储器回收生效的确定者 (finalizer)。
– 可以与 C#中的委派无缝配对的对象谓词值。
2004.11.3 AI程序设计 25
第 二 部分:第 6章 类与对象
6.12 Visual Prolog 5与 Visual Prolog 6的差异
? 句点
? 谓词
? 谓词论域
? 引用论域
? 函数子句
? 常量
? 事实
? 事实变量
这一节主要是针对 Visual Prolog 6和 Visual Prolog 5之间差异的一个
快速指南。焦点集中在 Visual Prolog 5已经被改变了的特性,而不是新增
加的特性。内容包括,
? 嵌套表达式与函数
? 编译器命令
? 条件编译
? 输入输出及特殊论域
? 省略与匿名参数类型
? 对象与类
? 库支持
2004.11.3 AI程序设计 26
第 二 部分:第 6章 类与对象
6.12.1 句点
? 句点( Dots),所有的声明(即常量,论域,谓词和事实)均
以句点(即,.”)终止。
2004.11.3 AI程序设计 27
第 二 部分:第 6章 类与对象
6.12.2 谓词
? 谓词( Predicates)声明的语法在各方面已经被改变。这个例子说明了
其大部分的改变。
predicates
ppp, (integer Input) procedure (i),
qqq, (integer Input) -> integer Output procedure (i),
? 在谓词名字 (在声明中总是第一个字 )和谓词类型之间有一个冒号。
? 返回类型 (即便要 ) 已经被移到箭头(即,->”)之后的类型表达式的尾部。
? 谓词的类型、样式和流模式之间没有破折号。
? 声明前面的谓词样式格式不再存在。
? 如果谓词在一个类或接口声明中进行声明,则所省略的谓词流模式意味着
所有的参数均为输入参数。
2004.11.3 AI程序设计 28
第 二 部分:第 6章 类与对象
6.12.3 谓词论 域
? 谓词论域( Predicate Domains)声明的语法同样已经被改变。
domains
pppDom = (integer Input) procedure (i),
qqqDom = (integer Input) -> integer Output
procedure (i),
2004.11.3 AI程序设计 29
第 二 部分:第 6章 类与对象
6.12.4 引用论 域
? 引用论域( Reference Domains)不能引用一个非引用型的用户
论域。
? 下面的 Visual Prolog 5代码,
domains
xref = reference xref(y)
y = integer
? 对应于下面的 Visual Prolog 6代码,
domains
xref = reference xref(yref),
yref = reference integer,
2004.11.3 AI程序设计 30
第 二 部分:第 6章 类与对象
6.12.5 函数子句
? 函数子句( Function Clauses)的语法已经改变,因此返回值被
放在子句头的等号之后。
clauses
qqq(X) = 1,-
X < 3,
!,
qqq(X) = X,
2004.11.3 AI程序设计 31
第 二 部分:第 6章 类与对象
6.12.6 常量
? 常量( Constants)不再是宏,但必须是一个确定类型的值。
constants
myList, integer_list = [14,56,-3],
? 对数字常量、字符常量和串常量而言,类型可以被忽略。
2004.11.3 AI程序设计 32
第 二 部分:第 6章 类与对象
6.12.7 事实
? 事实( Facts)声明类似于谓词声明,
facts - myFactDB
fact1, (integer Input) nondeterm,
? 注意,冒号以及样式声明(即 nondeterm)写在尾部。
? 事实仍然可以为 single,determ和 nondeterm。
? 事实段只能用在一个类的实现内部。
? 事实修饰符 nocopy不再使用。所有事实都作为 nocopy方式处理。
? 事实段以关键字 facts标识。可选择的 database不再是一个关键字。
2004.11.3 AI程序设计 33
第 二 部分:第 6章 类与对象
6.12.8 事实变量
? 在 Visual Prolog 6中,事实变量( Fact Variables)是一个新概念。
? 它们在事实段进行声明,
facts
myFactVariable, integer_list,= [14,56,-3],
?Visual Prolog 5的用户认为这等价于下面伴随有初始化的单个事实。
facts
myFact, (integer_list Value) single,
clauses
myFact([14,56,-3]),
2004.11.3 AI程序设计 34
第 二 部分:第 6章 类与对象
6.12.8 事实变量
? 事实变量用起来像变量一样,因为它来自于命令性( imperative)语言,
clauses
p(),-
myFactVariable,= [14,56,-3],
q(myFactVariable),
?Visual Prolog 5的用户认为这等价于下面的代码,
clauses
p(),-
assert(myFact( [14,56,-3])),
myFact(Value),q(Value),
2004.11.3 AI程序设计 35
第 二 部分:第 6章 类与对象
6.12.9 嵌套表达式与函数
? 嵌套的表达式与函数( Nested Expressions and Functions),
表达式与函数实际上在任何地方都可以嵌套。
clauses
factorial(0) = 1,-
!,
factorial(N) = N * factorial(N-1),
? 从上面的代码可以看到,第二个子句的返回值是一个表达式。这个
表达式包含一个函数调用,并且给这个函数的参数也是一个表达式。
2004.11.3 AI程序设计 36
第 二 部分:第 6章 类与对象
6.12.10 编译器命令
? 编译器命令( Compiler Directives)以字符,#”开始。例如,
#include @"packageAaa\packageAaa.ph"
? 一串文字前面的符号, @” 意味着转义序列不被使用,
即 @"\n"代表两个符号 "\"和 "n"。
2004.11.3 AI程序设计 37
第 二 部分:第 6章 类与对象
6.12.11 条件编译
? 条件编译( Conditional Compilation)只适用于 Visual Prolog
6中的段( sections)。
例如,
#if a::myconst = 1 #then
clauses
p(0) = 1,
#endif
2004.11.3 AI程序设计 38
第 二 部分:第 6章 类与对象
6.12.11 条件编译
? 这段 Visual Prolog 5代码,
clauses
p(X):-
ifdef
debug_mode
write("X",X),
endif
q(X),
? 对应于右边的 Visual Prolog 6代码。
1) 编译时间常量 debug_mode应当放在某
个类中。假设我们命名它为全局常量
globalConstants。
2)额外需要一个谓词来表示该子句的一部
分,这部分可能位于条件编译之内。
class predicates
writeX, (integer X),
#if globalConstants::debug_mode = 1
#then
clauses
writeX(X):-
stdIO::write("X=",X),
#else
clauses
writeX(_),
#endif
clauses
p(X):-
writeX(X),
q(X),
2004.11.3 AI程序设计 39
第 二 部分:第 6章 类与对象
6.12.12 输入输出及特殊论域
? 特殊论域指 file论域与 db_selector论域。 file论域放在
fileSelector类中。 db_selector论域放在 chainDBSelector类中。
文件 pfc\5xVIP\fileSelector.cl和文件 pfc\ChainDB\
chainDBSelector.cl应当在它们被修改之前复制到项目目录(适
当的子目录)。
? 引入论域只考虑向后的兼容性。新的风格是使用对象进行代替。
? 在 Visual Prolog 6中,IO处理是基于流的。流谓词使用匿名
( anonymous)参数类型和省略( ellipsis)符号。
2004.11.3 AI程序设计 40
第 二 部分:第 6章 类与对象
6.12.13 省略与匿名参数类型
? 如果许多参数是可变的,则可以使用省略号( ellipsis)。省略号是一个特
殊的符号,代表“任意类型的零个或多个参数”。其语法为,
class predicates
p, (...),
clauses
p(...):-
stdio::write(...),
? 如果一个参数的类型在编译时间是未知的,则可以使用匿名
( anonymous)参数。其语法为,
class predicates
setProperty, (_),
2004.11.3 AI程序设计 41
第 二 部分:第 6章 类与对象
6.12.14 对象与类
? Visual Prolog 5与 Visual Prolog 6对象模型之间的差别已在前面
叙述了。
2004.11.3 AI程序设计 42
第 二 部分:第 6章 类与对象
6.12.15 库支持
? 库支持叙述 Visual Prolog 6与 Visual Prolog 5之间名字的等价性。
? 转换表(参见表 6- 1)描述了 Visual Prolog 6与 Visual Prolog 5
之间的等价名字。
? 一般来说,库是按照对象概念新编写的。有一个特殊的库 5xVip,
包括子文件夹。该库的目的是用来移植 Visual Prolog 5代码。新的
程序不应该使用 5xVip库。
2004.11.3 AI程序设计 43
第 二 部分:第 6章 类与对象
本章小结
本章主要介绍 Visual Prolog 6中的对象模型, 类
实体, 模块, 创建和访问对象, 接口对象类型, 包容多
态性, Support类型扩展, Object超类型, 继承, 以及
Visual Prolog 5与 Visual Prolog 6的差别等 。 这些内
容是 Visual Prolog 6的面向对象的基本概念 。
2004.11.3 AI程序设计 44
第 二 部分:第 6章 类与对象
习 题
1,试解释 Visual Prolog 6中的对象模型, 类实体, 模块等基本概念及其含
义 。
2,在 Visual Prolog 6中, 如何创建和访问对象? 何谓接口对象类型?
3,何谓, 多重实现,? 其含义是什么?
4,什么叫, 包容多态性,? 其含义是什么?
5,什么叫, 继承,? 其含义是什么?
6,何谓 Support类型扩展, 何谓 Object超类型?
7,Visual Prolog的对象体系与其他语言有何区别与联系?
8,Visual Prolog 5与 Visual Prolog 6有何差异?
9,如何将 Visual Prolog 5的程序移植到 Visual Prolog 6?