70
2004.11.1 AI程程程程 1 程程程程 12 Visual Prolog 程程程程 程 12 程 Visual Prolog 程程程程 12.1 12.2 程程 12.3 程程 12.4 程程 12.5 程程 12.6 程程 12.7 程程程 程程程程 程程程程

第 12 章 Visual Prolog 程序元素

  • Upload
    kamil

  • View
    98

  • Download
    4

Embed Size (px)

DESCRIPTION

第 12 章 Visual Prolog 程序元素. 12.1 项 12.2 常量 12.3 谓词 12.4 子句 12.5 事实 12.6 评估 12.7 程序段 本章小结 本章习题. 12.1 项. 本章介绍 Visual Prolog 的程序元素,内容包括项( Terms )、常量、谓词、子句、事实、运算、程序段等。. 12.1.1 项的基本概念. 项有两种类型:公式 formulas 和表达式 expressions 。 表达式代表数值,比如数字 7 ;公式代表逻辑声明,比如 “ 数字 7 比数字 3 大 ” 。 - PowerPoint PPT Presentation

Citation preview

2004.11.1 AI程序设计 1

第三部分:第 12 章 Visual Prolog 程序元素

第 12 章 Visual Prolog 程序元素 12.1 项 12.2 常量 12.3 谓词 12.4 子句 12.5 事实 12.6 评估 12.7 程序段 本章小结 本章习题

2004.11.1 AI程序设计 2

第三部分:第 12 章 Visual Prolog 程序元素

12.1 项

本章介绍 Visual Prolog 的程序元素,内容包括项( Terms )、常量、谓词、子句、事实、运算、程序段等。

2004.11.1 AI程序设计 3

第三部分:第 12 章 Visual Prolog 程序元素

12.1.1 项的基本概念 项有两种类型:公式 formulas 和表达式 expressions 。表达式代表数值,比如数字 7 ;公式代表逻辑声明,比如“数字 7 比数字 3 大” 。 下面是项的简化定义,其中包括非法的语法结构。例如, ! + ! 的书写形式是不合法的。但是,相信在和语言概念的直觉理解相结合时,使用这样的简化语法表示,在大多数情况下对于类型系统和运算符层次结构的描述更为有利。

2004.11.1 AI程序设计 4

第三部分:第 12 章 Visual Prolog 程序元素

12.1.1 项的基本概念term:  ( term )    unaryOperator term    term binaryOperator term    literal    identifier    qualifiedName    globalName    memberAccess    predicateCall    cut    ellipsis    factvariableAssignment

文字有通用类型:literal: stringLiteral characterLiteral integerLiteral realLiteral binaryLiteral cut:    !

2004.11.1 AI程序设计 5

第三部分:第 12 章 Visual Prolog 程序元素

谓词调用 一个谓词调用形式如下:predicateCall: term ( term-comma-sep-list-opt ) 首项必须直接声明调用谓词的名字。就是说,首项必须是一个谓词名,一个限定谓词名或一个成员访问。注意,有些谓词有返回值,有些则没有返回值。如果一个谓词有返回值,这个值就必须在上下文中使用,不能被忽略。

12.1.1 项的基本概念

2004.11.1 AI程序设计 6

第三部分:第 12 章 Visual Prolog 程序元素

事实赋值 赋值操作符 := 用于向事实变量 factVariable 赋予一个新值。项 term 必须被赋于一个适当类型(即与事实变量或子类型相同的类型)的值。factVariableAssignment: factVariable := term

12.1.1 项的基本概念

2004.11.1 AI程序设计 7

第三部分:第 12 章 Visual Prolog 程序元素

12.1.2 运算符 运算符( Operators )按优先层次进行组织。在规则中,下面各组中的操作符具有相同的优先权,并且上面的操作符比下面的优先级高。就是说,一元加减法要比乘法运算符优先级高,而乘法运算符又比加法运算符高。圆括号可以改变运算优先级。unaryOperator: - +binaryOperator: multiplicationOperator additionOperator relationOperator andOperator orOperator

2004.11.1 AI程序设计 8

第三部分:第 12 章 Visual Prolog 程序元素

12.1.2 运算符 12.1.2.1 算术运算符 算术运算符( Arithmetic Operators )用于数字的算术运算。它们是表达式,用表达式作为参数。它们采用根类型作为参数,并返回通用类型的结果。(参见通用类型和根类型)所有的算术操作符都是左结合的( left associative )。 multiplicationOperator: one of * / div mod additionOperator: one of + -

2004.11.1 AI程序设计 9

第三部分:第 12 章 Visual Prolog 程序元素

12.1.2 运算符 12.1.2.2 关系运算符关系运算符( Relational Operators )是公式,用表达式作参数。它们从本质上是无关联的。relationOperator: one of > < > = <= <> >< =

2004.11.1 AI程序设计 10

第三部分:第 12 章 Visual Prolog 程序元素

12.1.2 运算符 12.1.2.3 逻辑运算符逻辑运算符( Logical Operators )主要包括逻辑“与( and )”、逻辑“或( or )”及逻辑“非( not )” 运算符等。逻辑“与”运算符 andOperator 和逻辑“或”运算符 orOperator 是公式,用公式作参数。它们是左结合的。 " , " 和 " and " 是同义词, " ; " 和 " or " 也是同义词。

andOperator: one of , and orOperator: one of ; or

2004.11.1 AI程序设计 11

第三部分:第 12 章 Visual Prolog 程序元素

12.1.2 运算符运算符举例: 以下的项7 + 3 * 5 * 13 + 4 + 3 = X / 6 ; A < 7, p(X) 与下面的项含义相同: ((((7 + ((3 * 5) * 13)) + 4) + 3) = (X / 6)) ; ((A < 7) , p(X)) 也就是说,项的最外面一层是两个项的“ or” ,而第一项是一个 (=)

关系项,第二项是 "and" 关系项。

2004.11.1 AI程序设计 12

第三部分:第 12 章 Visual Prolog 程序元素

12.1.3 类成员访问 类实体通过限定类名的方式进行访问:qualifiedName: identifier :: identifier

这样的限定名像普通的名字一样使用,即如果它是一个谓词,它就可以用于一个参数集。 有些名字访问不需要限定,参见有关作用域的内容。

2004.11.1 AI程序设计 13

第三部分:第 12 章 Visual Prolog 程序元素

12.1.4 对象成员访问 每当引用一个对象时,都可以访问该对象的对象成员谓词。

memberAccess: term : identifier

( 目前,项 term 必须是一个变量或一个事实变量 ) 标识符 identifier 必须是项 term 的类型。 在一个实现内部,对象成员谓词不需要引用对象就可以被调用,因为 "This"已经被包含在其中了。参见有关作用域的内容。

2004.11.1 AI程序设计 14

第三部分:第 12 章 Visual Prolog 程序元素

12.1.5 全局实体的访问 存在于 Visual Prolog 中的仅有的全局实体是类、接口和内部论域、谓词、常量。全局名在任意作用域内都可以直接访问。也可能存在全局名与局域名或输入名重合的情况。在这种情况下,全局实体可以用双冒号‘ ::’来限定(不带前缀的类名或接口名)。双冒号可以随处使用,但是最重要的用处还是接口名用作形式参数类型说明符的情况。 globalName: :: identifier

2004.11.1 AI程序设计 15

第三部分:第 12 章 Visual Prolog 程序元素

12.1.6 论域、算符和常量访问 论域、算符和常量都像类成员一样被访问。即便它们在一个接口中被声明。 这就是说,当它们要被限定的时候,就总是以类或接口名加双冒号来限定。

2004.11.1 AI程序设计 16

第三部分:第 12 章 Visual Prolog 程序元素

本节介绍常量( Constant )的有关概念,内容包括常量段、常量定义等。

12.2 常量

2004.11.1 AI程序设计 17

第三部分:第 12 章 Visual Prolog 程序元素

12.2.1 常量段 一个常量段( Constants Section )定义了当前作用域内的常量集。 constantsSection : constants constantDefinition-dot-term-list-opt

2004.11.1 AI程序设计 18

第三部分:第 12 章 Visual Prolog 程序元素

12.2.2 常量定义 常量定义( Constant Definitions )声明一个命名的常量,包括它的类型和值。constantDefinition : constantName : typeName = constantValueconstantName : lowerCaseIdentifier 常量值 constantValue 是一个表达式,在编译时间内计算。常量名 ConstantName应该是一个小写标识符 lowerCaseIdentifier 。如果一个类型名 typeName论域是一个标准论域,那么它和冒号 ' : '可以被省略,得到以下简写形式:

2004.11.1 AI程序设计 19

第三部分:第 12 章 Visual Prolog 程序元素

12.2.2 常量定义 constantDefinition : constantName = constantValue 以这样的方式定义的常量可以用于所有的上下文中,在这里可以使用与其同一种类的文字。 如果类型名 typeName 被省略,那么常量论域必须明确地被常量值表达式确定。仅在下列内部论域情况下, typeName才能被省略。 a )数字(整数或实数)常量。在这种情况下,相应的匿名数字论域被采纳为常量(详细情况参见数字论域) b )二进制常量。 c )字符串常量。 d )字符常量。

2004.11.1 AI程序设计 20

第三部分:第 12 章 Visual Prolog 程序元素

12.3 谓词

本节介绍谓词( Predicates )的有关概念,内容包括谓词段( Predicates Sections )、构造段( Constructors Sections )、接口谓词( Predicates from Interface )、谓词的元( Arity )等。

2004.11.1 AI程序设计 21

第三部分:第 12 章 Visual Prolog 程序元素

12.3.1 谓词段 谓词段声明当前作用域内的对象或类谓词的集合。 predicatesSection : class-opt predicates predicateDeclaration-dot-term-list-opt 关键字 class只能在类实现内部使用,这是因为在接口中声明的谓词永远是对象谓词;在类声明中声明的谓词永远是类谓词。

2004.11.1 AI程序设计 22

第三部分:第 12 章 Visual Prolog 程序元素

12.3.1 谓词段谓词声明 类声明用于声明作用域中的谓词,在作用域中这些谓词是可见的。当谓词在一个接口定义中被声明时,就是说相应类型的对象必须支持这些谓词。当谓词在类声明中被定义时,就是说该类提供所声明的公用谓词。并且,如果谓词在类的实现中被声明的话,那么该谓词就在局部可用。在所有的情况下,必须存在谓词的相应定义。predicateDeclaration : predicateName : predicateDomain linkName-opt predicateName : predicateDomainName linkName-optlinkName : as stringLiteralpredicateName : lowerCaseIdentifier这里 predicateDomainName 是在论域段声明的谓词论域名。

2004.11.1 AI程序设计 23

第三部分:第 12 章 Visual Prolog 程序元素

12.3.1 谓词段 一个谓词声明,声明了该谓词的名称以及类型、模式、流(参见谓词论域)和可选的一个连接名。 只有类谓词可以有连接名。如果没有声明连接名,那么就从谓词名取连接名,取名的方式取决于调用约定。 如果调用约定是 apicall ,那么 as 子句中所声明的连接名就可以采用任意方式修饰;如果该修饰不是所要的,则用 stdcall 代替。

2004.11.1 AI程序设计 24

第三部分:第 12 章 Visual Prolog 程序元素

12.3.2 构造段 构造段声明了构造器的集合。这些构造器属于所出现的构造段的作用域(参见类声明和类实现)。constructorsSection : constructors constructorDeclaration-dot-term-list-opt 构造段只能出现在构造对象的类的声明和实现当中。

2004.11.1 AI程序设计 25

第三部分:第 12 章 Visual Prolog 程序元素

12.3.2 构造段构造声明 构造声明用于声明类的已命名的构造器。 实际上,构造器包含两个相关谓词。 * 一个类函数,返回一个新的被构建的对象; * 一个对象谓词,在初始化继承对象时使用。 一个相关的对象谓词构造器用于执行一个对象的初始化。该谓词只能从该类自身的构造器或是该类的继承类的构造器中调用(即基本的类初始化)。constructorDeclaration : constructorName : predicateDomain 给构造器声明谓词模式是非法的,构造器总是过程模式的。

2004.11.1 AI程序设计 26

第三部分:第 12 章 Visual Prolog 程序元素

12.3.2 构造段举例考虑下面的类:

class test_class : test constructors new : (integer Argument) .end class test_class

相关的类级的谓词形式如下(有以下标志):class predicates new : (integer) -> test.

而相关的对象级的谓词形式如下:predicates new : (integer).

2004.11.1 AI程序设计 27

第三部分:第 12 章 Visual Prolog 程序元素

12.3.2 构造段再考虑下面的实现:

implement test2_class inherits test_class clauses new() :- test_class::new(7), % invoke the base class constructor on "This" p(test_class::new(8)). % create a new object of the base class and pass it to p(...) ...

第一次调用 test_class::new不返回值,因此这是对构造器的非函数对象的一次调用。就是说,这是基本类构造器 "This" 的一次调用。第二次调用返回一个值,因此它是对类的构造器的函数调用。就是说,创建了一个新的对象。

2004.11.1 AI程序设计 28

第三部分:第 12 章 Visual Prolog 程序元素

12.3.3 接口谓词 一个接口能够通过在 predicates from 段声明的谓词来支持另一接口的子集。 predicates from 段指定接口和所有支持的谓词。这些谓词以名字或者名字和元数来声明。 如果一个接口支持另一个接口的子集,那么它就不是与另外那个接口相关的子类型或超类型。 关于 predicates from 段重要的是,所提及的谓词保留它们的原始接口。因此: * 来自原始接口的任意谓词不会发生支持冲突; * 它们能够作为来自原始接口的谓词被继承。 predicatesFromInterface : predicates from interfaceName predicateNameWithArity-comma-sep-list-optpredicatesFromInterface只能在接口定义中被使用。

2004.11.1 AI程序设计 29

第三部分:第 12 章 Visual Prolog 程序元素

12.3.3 接口谓词举例interface aaa predicates ppp : (). qqq : ().end interface aaainterface bbb predicates from aaa ppp predicates rrr : ().end interface bbbinterface ccc supports aaa, bbbend interface ccc 即使 aaa 和 bbb 都声明了谓词 ppp ,但 ccc 可以不产生任何冲突地支持二者。这是因为 ppp 在所有情况下都含有 aaa ,将其作为原始接口。

2004.11.1 AI程序设计 30

第三部分:第 12 章 Visual Prolog 程序元素

12.3.3 接口谓词举例interface aaa predicates ppp : (). qqq : ().end interface aaainterface bbb predicates from aaa ppp predicates rrr : ().end interface bbbclass aaa_class : aaaend class aaa_classclass bbb_class : bbbend class bbb_classimplement aaa_class inherits bbb_class clauses qqq().end implement aaa_class

aaa_class 可以从 bbb_class 继承 ppp ,因为 ppp 在两个类中都含有 aaa ,并将其作为原始接口。

2004.11.1 AI程序设计 31

第三部分:第 12 章 Visual Prolog 程序元素

12.3.4 变元 使用 N个参数的谓词被称为 N元谓词( N-ary ),或者说该谓词有 N个变元。所含变元数不同的谓词,即使它们名称相同,通常也是不同的谓词。 在大多数情况下,一个谓词的元数在包含该谓词的上下文中是明显的。但是,在某些情况,比如,接口谓词段 predicatesFromInterface 段和resolve 限定中,变元数并不明显。 为了区别 predicates from 段和 resolve 限定中不同变元数的谓词,谓词名可以选择采用带有变元数的声明。

2004.11.1 AI程序设计 32

第三部分:第 12 章 Visual Prolog 程序元素

12.3.4 变元下列变元数是可能的:· Name/N 指一个普通谓词 (即不是一个函数 )的名字,变元个数为 N;· Name//N 指一个函数名,变元个数为 N;· Name/N... 指一个带 N个变元的普通谓词名,后跟一个省略参数 (即个数可改变的参数 ) Name//N... 指一个带N个变元的函数名,后跟一个省略参数。

2004.11.1 AI程序设计 33

第三部分:第 12 章 Visual Prolog 程序元素

12.3.4 变元predicateNameWithArity : predicateName arity-opt arity : / integerLiteral ellipsis-opt // integerLiteral ellipsis-opt在 Name/0... 和 Name//0... 中, 0是可选项,因此它们可以分别写作Name/... 和 Name//... 。注意,省略号 "…" 可以作为最后一个形式参数用于谓词和谓词论域的声明。在这种情况下,就是指所声明的谓词(或谓词论域)的参数个数是可改变的。省略流必须与一个省略参数匹配,因此只能是流模式中的最后一个流。

2004.11.1 AI程序设计 34

第三部分:第 12 章 Visual Prolog 程序元素

12.4 子句 本节介绍子句( Clauses )的有关内容,包括子句段( Clauses Sections )、目标段( Goal Sections )等。

2004.11.1 AI程序设计 35

第三部分:第 12 章 Visual Prolog 程序元素

12.4.1 子句段 子句段由子句集组成。子句段包括谓词的实现或事实的初始化值。 一个单独的子句段可以含有几个谓词和事实的子句。另一方面,同一谓词或事实(同名并变元数相同)的所有子句必须集中在一个子句段中,并且不涉及其它谓词或事实的子句。 clausesSection : clauses clause-dot-term-list-opt 子句用于定义谓词。单一的谓词由一个子句集定义。每个子句依次执行,直到其中一个子句成功,或没有子句可执行为止。如果没有子句成功,该谓词失败。 如果一个子句成功,并且在一个谓词的剩余部分有更多相关子句,那么程序控制将在以后回溯到该谓词的子句,以查找其它的解决方案。

2004.11.1 AI程序设计 36

第三部分:第 12 章 Visual Prolog 程序元素

12.4.1 子句段 子句由一个子句头( head )和一个可选的子句体( body )组成。clause : clauseHead returnValue-opt clauseBody-optclauseHead : lowercaseIdentifier ( term-comma-sep-list-opt )returnValue : = termclauseBody : :- term

2004.11.1 AI程序设计 37

第三部分:第 12 章 Visual Prolog 程序元素

12.4.2 目标段 目标段是一个程序的入口。当程序开始执行时,首先从目标段开始执行,目标段被执行完后,程序就退出。 goalSection : goal term . 目标段由一个子句体组成。目标段定义了它自身的作用域,因此所有的调用都应当包含类的限定符。 通常,目标必须是过程模式。

2004.11.1 AI程序设计 38

第三部分:第 12 章 Visual Prolog 程序元素

本节介绍事实( Facts )的有关内容,包括事实段( Facts Sections )、事实声明( Fact Declarations )、事实变量( Fact Variables )等。

12.5 事实

2004.11.1 AI程序设计 39

第三部分:第 12 章 Visual Prolog 程序元素

12.5.1 事实段 一个事实段声明一个由若干事实组成的事实数据库。该事实数据库及事实属于当前作用域。 事实数据库可以存在于类级别上,也可以存在于对象级别上。 事实段只能在类实现中进行声明。 factsSection : class-opt facts factsSectionName-opt factDeclaration- dot-term-list-opt factsSectionName : - lowerCaseIdentifier

2004.11.1 AI程序设计 40

第三部分:第 12 章 Visual Prolog 程序元素

12.5.2 事实声明 事实声明用于声明一个事实数据库的事实。事实声明也是一个事实变量或一个事实算符。 factDeclaration : factVariableDeclaration factFunctorDeclaration factFunctorDeclaration : factName : ( argument-comma-sep-list-opt ) factMode-opt factName : lowerCaseIdentifier 一个事实算符声明缺省为 nondeterm 事实模式。

2004.11.1 AI程序设计 41

第三部分:第 12 章 Visual Prolog 程序元素

12.5.2 事实声明 一个事实算符可以通过子句段进行初始化。在这种情况下,子句中的值应当是表达式,这些表达式可以在编译时间内进行求值。 factMode : one of determ nondeterm single 如果模式为 single ,那么一个事实就有一个值并且只有一个值,而且谓词 assert会给原来的值赋新的值。谓词 retract 不用于单个事实。 如果模式为 nondeterm ,那么这一事实就可以有 0个、 1 个或任意其它个值。如果模式为 determ ,那么事实可以有 0或 1 个值。如果事实有 0个值,那么任何读操作都会失败。

2004.11.1 AI程序设计 42

第三部分:第 12 章 Visual Prolog 程序元素

12.5.3 事实变量 一个事实变量与一个一元单个事实算符类似。但是,从语法上讲,它作为可变变量(即以赋值方式)使用。 factVariableDeclaration : factVariableName : domain initialValue-opt initialValue : := constantValue factVariableName : lowerCaseIdentifier 一个常量值 constantValue应当是一个项(论域类型的),可以在编译时间求值。 只要在一个构造器中将事实变量初始化,那么常量值就可以省略。类事实变量应当总有一个初始的常量值。

2004.11.1 AI程序设计 43

第三部分:第 12 章 Visual Prolog 程序元素

12.5.3 事实变量 注意,关键字 erroneous 可被用来将其值赋给事实变量。下面两行是有效的: facts thisWin : vpiDomains::windowHandle := erroneous. clauses p() :- thisWin := erroneous. 用 erroneous 赋值的基本思想,是为了在某些代码错误地使用了未初始化的事实时,给出一个明确的运行时间错误。

2004.11.1 AI程序设计 44

第三部分:第 12 章 Visual Prolog 程序元素

12.5.4 事实 事实只能在类实现中进行声明,并且以后它们只能从这个实现被引用。因此事实的作用域就是它们被声明的这个实现。但是对象事实的生命期是它所属的对象的生命期。同样地,类事实的生命期是从程序开始到程序结束。举例 下面的类声明了一个对象事实 objectFact 和一个类事实 classFact :implement aaa_class facts objectFact : (integer Value) determ. class facts classFact : (integer Value) determ. ...end implement aaa_class

2004.11.1 AI程序设计 45

第三部分:第 12 章 Visual Prolog 程序元素

12.6 评估 评估( Evaluation )又称为求值运算。 Visual Prolog 通过执行目标来实现。 目标是一个项。本节叙述项和子句的执行或计算是怎样进行的,包括回溯( Backtracking )、谓词调用、合一( unification )、引用论域、匹配、嵌套的函数调用、变量与常量、算术表达式、事实断言与撤消等。

2004.11.1 AI程序设计 46

第三部分:第 12 章 Visual Prolog 程序元素

12.6 评估12.6.1 回溯12.6.2 谓词调用12.6.3 合一12.6.4 引用论域12.6.5 匹配12.6.6 嵌套的函数调用12.6.7 变量与常量12.6.8 算术表达式12.6.9 事实断言与撤消

12.6.10 失败谓词和 成功谓词12.6.11 逻辑与12.6.12 逻辑或12.6.13 逻辑非12.6.14 截断12.6.15 谓词 finally/2

2004.11.1 AI程序设计 47

第三部分:第 12 章 Visual Prolog 程序元素

12.6.1 回溯 一个 Prolog 程序的评估或运算是搜索求解的过程。搜索求解的每一步或者成功或者失败。在程序执行的特定点上,有可能不止有一种解决方案。当遇到这样的选择点时,就建立所谓的回溯点。一个回溯点是程序状态的一个记录,即添加一个指针到未执行的选择点。如果它证明了初始的选择不能提供解决方案(即失败),那么程序将回溯到记录过的回溯点,从而恢复程序状态和追踪另一个选择。在下面的部分还将对该机制进行详细描述和解释。

2004.11.1 AI程序设计 48

第三部分:第 12 章 Visual Prolog 程序元素

12.6.2 谓词调用 通过使用参数到一个谓词实现该谓词的调用。该谓词必须有一个流模式,以匹配参数的自由或绑定状态。每个谓词由一个子句集合(或在外部用某种其它语言)定义。 当谓词被谓词调用引用时,每个子句依次执行直到它们成功,或直到没有子句可执行为止。如果没有子句成功,那么该谓词失败。 如果一个子句成功并且还剩有其他的有关子句,那么程序控制可以在以后回溯到剩余的子句,以搜索其它的解。

2004.11.1 AI程序设计 49

第三部分:第 12 章 Visual Prolog 程序元素

12.6.2 谓词调用举例clauses ppp() :- qqq(X), write(X), fail. qqq(1). qqq(2). qqq(3).

当 ppp 被调用时,它依次调用 qqq。当 qqq被调用,它首先建立一个指向第二个子句的回溯点。然后执行第一个子句。因此 ppp中的自由变量 X 与数字 1匹配,从而 X 被绑定为 1 。 在 ppp执行时, X (即 1 )被写出后,由 fail援引回溯点。从而程序控制被设置到 qqq中的第二个子句,并且程序状态被设置回 qqq首次进入的状态,即 ppp 的 X 不再被绑定。 在 qqq中的第二个子句实际执行之前,第三个子句的回溯点已经建立好了。

2004.11.1 AI程序设计 50

第三部分:第 12 章 Visual Prolog 程序元素

12.6.3 合一 当一谓词被调用时,来自调用的参数与每个子句的子句头的项合一。 合一是绑定变量的过程,在这一过程中,两项通过尽可能少的绑定达到相等(即为进一步绑定留下尽可能多的开放空间)。 变量可以绑定为各种项,包括变量或含有变量的项。 合一有时可能有时不可能,也就是说它可能成功也可能失败。 变量和与其合一的项是有类型的,一个变量只能被绑定到与它类型相同的项或子类型上。当两个变量互相绑定时,它们就必须是完全相同的类型。 如上所述,合一发生在一个谓词调用和子句头之间,也发生在比较两项是否相等之时。

2004.11.1 AI程序设计 51

第三部分:第 12 章 Visual Prolog 程序元素

12.6.3 合一举例 考虑类型相同的两项: T1 = f1(g(), X, 17, Y, 17) T2 = f1(Z, Z, V, U, 43) 我们打算从左至右合一这两项。 T1 和 T2 都是 f1/5 项,这是匹配。因此打算将 T1 中的参数与T2 中相应的参数进行合一。

首先必须合一 Z和 g(),产生了合一器中的第一个绑定: Z = g()其次是 X 和 Z,其中 Z 已经被绑定到 g()。如果将 X 也绑定到 g(),那么这两个参数也可以合一。因此现在对于合一器有如下的新增内容: X = Z = g()再次,必须将 V绑定为 17 ,然后将 Y绑定到U。再合并到合一器中得: X = Z = g(); V = 17 ; Y = U现在这两个合一的项等同于下面的项: T1 = f1(g(), g(), 17, Y, 17) T2 = f1(g(), g(), 17, Y, 43)

结论:最后两个参数 17 和 43 不能进行合一,所以合一失败。 T1 和 T2 不能合一。

2004.11.1 AI程序设计 52

第三部分:第 12 章 Visual Prolog 程序元素

12.6.4 引用论域下面举例说明引用论域的概念和用法。举例 考虑类型相同的两项:T1 = f1(g(), X, 17, Y)T2 = f1(Z, Z, V, U) 这些项可以依照以下合一器进行合一:X = Z = g()V = 17Y = U 并且合一后的项如下:f1(g(), g(), 17, Y) 这一项包括一个自由变量 Y(它正好绑定到U)。如果项的类型为引用论域,那么它们只能是自由变量。

2004.11.1 AI程序设计 53

第三部分:第 12 章 Visual Prolog 程序元素

12.6.5 匹配 对于不是引用论域类型的项不需要进行完全彻底的合一,用匹配就足够了。如果该项包括引用论域类型的子项,那么这些子项必须合一而不是匹配。 除了变量只能被绑定到基础( grounded )项之外,匹配与合一是相同的。一个基础项是不包含任何自由变量的项。 为谓词声明适当的流模式,就有可能使用匹配而不是完全彻底地进行合一。

2004.11.1 AI程序设计 54

第三部分:第 12 章 Visual Prolog 程序元素

12.6.5 匹配举例 clauses ppp(Z, Z, 17). qqq() :- ppp(g(), X, 17). ppp 调用与 ppp 子句的合一可能使用下面的合一器: Z = X = g() 如果 ppp 有流模式 (i,o,i),那么合一就是一个匹配: * g()作为第一个参数输入,绑定到Z * 子句中的第二个参数因此被绑定;这样输出 X , X 被绑定到 g()。 * 最后第三个参数 17 作为输入,这个数字只是与子句中的第三个参数进行比较。 只有通过流模式才能预测子句不需要真正合一的可能性。

2004.11.1 AI程序设计 55

第三部分:第 12 章 Visual Prolog 程序元素

12.6.6 嵌套的函数调用 彼此必须相互合一或匹配的项允许包含子项,这些子项实际上是那些必须在合一或匹配完成之前进行求值计算的表达式或函数调用。这样的子项求值计算建立在需要的基础上。 在谓词调用中,所有输入参数均在调用发生前进行计算,所有输出参数均为变量,不需要计算。 在匹配或合一能被确定之前,子句头也可以包含必须计算的项。 总的说来,基本原则是: * 输入在另一匹配之后,在子句体计算之前; * 输出在子句体计算之后; * 从左至右。

2004.11.1 AI程序设计 56

第三部分:第 12 章 Visual Prolog 程序元素

12.6.7 变量与常量 Prolog 中的变量是不可变的,一旦被绑定了一个值就会保持该值,除非利用回溯在恢复先前程序状态的过程中重新释放这个变量。 一个变量可以在合一和匹配时被绑定,如果它已经被绑定,那么可以经过计算得出它所绑定的值。 变量名以大写字母或下划线开头,后面可以跟任意多个字母(大小写均可)、数字或下划线字符。例如,下面的变量名是合法的:My_first_correct_variable_name__Sales_10_11_86 下面两个变量名是非法的:1stattemptsecond_attempt 一个常量( Constants )表示它被定义的值。

2004.11.1 AI程序设计 57

第三部分:第 12 章 Visual Prolog 程序元素

12.6.8 算术表达式 算术表达式( Arithmetic Expressions )按算术运算符中所描述的运算符级别进行分析。对于二元表达式先计算左边的项,再计算右边的项。相等( = ) 首先计算等式左边的项,然后计算右边的项,最后它们相互合一。合一常常可以简化为匹配。比较 首先计算左边的项,然后计算右边的项,最后比较结果。 注意:不等号( <>或 ><)不是等号( = )的对偶运算。 <>比较两个值,而= 是将两项合一(至少在大多数情况下)。 表达式 A=B的对偶表达式为 not(A=B)。

2004.11.1 AI程序设计 58

第三部分:第 12 章 Visual Prolog 程序元素

12.6.9 事实断言与撤消 事实变量( Fact Variables )是可变变量,它们计算已经赋予它们的值。 一个新值可以通过赋值的方法赋给一个事实变量。 一个事实数据库包括若干完全用具体例证说明的谓词头,该谓词头与来自事实段定义的事实相对应。这些事实可以通过用事实名作为谓词名的谓词调用来访问。该谓词调用依次与每个事实相匹配;每当该谓词调用与事实相匹配时,后面就会跟一个下一事实可能的回溯点。当事实数据库中不再有事实时,该谓词调用失败。

2004.11.1 AI程序设计 59

第三部分:第 12 章 Visual Prolog 程序元素

12.6.10 失败谓词和成功谓词 失败谓词 fail/0和成功谓词 succeed/0是两个内建的空变元谓词。 fail/0 总是失败而 succeed/0 总是成功,此外它们并无任何影响。

2004.11.1 AI程序设计 60

第三部分:第 12 章 Visual Prolog 程序元素

12.6.11 逻辑与 逻辑“与 (and)” ,在程序的子句体中常用逗号表示。一个“ and”表达式“ A,B” 按如下步骤进行处理。首先计算左子项 A 。如果这一计算失败,整个“ and” 项失败。如果 A 成功则接着计算右子项。如果计算失败,则整个“ and” 项失败,否则“ and” 项成功。因而,只有在第一子项 A 成功的情况下,第二子项 B才会得到计算。举例clauses ppp() :- qqq(), rrr(). 当 ppp 被调用时,它首先调用 qqq,如果 qqq成功,则调用 rrr 。如果 rrr 成功,则“ and” 项成功,随后即整个子句成功。

2004.11.1 AI程序设计 61

第三部分:第 12 章 Visual Prolog 程序元素

12.6.12 逻辑或 逻辑“或( or )”,在程序的子句体中常用分号表示。一个“ or” 表达式“ A;B” 按如下步骤进行处理。首先创建一个指向第二项 B的回溯点,然后计算第一项 A 。如果第一项计算成功,则整个“ or” 项成功,留下一指向第二项 B的回溯点。如果第一项的计算失败,则指向第二项的回溯点被激活。 如果指向第二项的回溯点被激活(因第一项失败或在后面执行的某项操作中调用了回溯点),则第二项 B被计算,若B成功,则整个“ or” 项成功。 因而一个“ or” 项可以在一个回溯点上获得成功,而第二子项 B只在回溯过程中得到计算。

2004.11.1 AI程序设计 62

第三部分:第 12 章 Visual Prolog 程序元素

12.6.12 逻辑或举例clauses ppp() :- qqq(V), write(V), fail. qqq(X) :- X = 3 ; X = 7.

因为创建了回溯点,回溯也使所有变量绑定失效。在这种情况下,意味着ppp 中的 V 及 qqq中的 X 都是非绑定变量。然后, X=7 在 qqq中得到计算, X因而绑定到 7 ,控制回到 ppp , X绑定到 7 并被打印输出。接着再次出现失败,这次 ppp 中没有更多的回溯点,所以 ppp失败。

当 ppp 被调用时,它首先调用 qqq。 qqq 先创建一个指向 X=7 的回溯点,然后计算 X=3 。 qqq 返回。因为 ppp 中的 V绑定为 3 , V 被打印输出为 3 ,然后出现 fail 。 fail总是失败,因此控制权被设定在 qqq的回溯点上。

2004.11.1 AI程序设计 63

第三部分:第 12 章 Visual Prolog 程序元素

12.6.13 逻辑非 逻辑“非( not )”, not/1 以一个项作为参数。对 not(A)的计算首先计算 A 。如果 A 成功,则 not(A) 失败,如果 A失败,则 not(A)成功。 注意: not(A)不绑定任何变量,因为若 not(A)成功,则 A失败,一个失败的变量不绑定任何量;另一方面,若 not(A) 失败,它同样不能绑定任何变量,因为该项本身失败。另外, not(A)也不能跟任何回溯点,因为若 not(A)成功,则 A失败,而一个失败的项不能包含任何回溯点。这也意味着 A 中所有成功的可能性都没有了。

2004.11.1 AI程序设计 64

第三部分:第 12 章 Visual Prolog 程序元素

12.6.14 截断( Cut ) 截断,在程序中常用感叹号“!”表示。截断会删除从当前谓词入口处开始创建的所有回溯点,这是指所有指向后来子句的回溯点,再加上截断之前的当前子句的谓词调用中安置的回溯点。 举例clauses ppp(X) :- X > 7, !, write("Greater than seven"). ppp(_X) :- write("Not greater than seven").

当 ppp 被执行时,有一个首次创建的指向第二子句的回溯点,然后第一子句被执行。如果测试“ X>7” 成立,则截断(“ !” )是可到达的。这一截断“ !”将取消指向第二子句的回溯点。

2004.11.1 AI程序设计 65

第三部分:第 12 章 Visual Prolog 程序元素

12.6.14 截断( Cut )举例clauses ppp() :- qqq(X), X > 7, !, write("Found one"). ppp() :- write("Did not find one").clauses qqq(3). qqq(12). qqq(13).

当 ppp 被执行时,它首先创建一个指向第二个 ppp 子句的回溯点,然后调用 qqq。qqq 将创建一个指向第二个 qqq子句的回溯点并执行第一子句,从而返回值 3 。在 ppp 中,变量 X绑定为 3 这个值然后与 7 比较。测试失败,因而控制回溯至 qqq的第二子句。在执行第二子句前,指向 qqq的第三子句的回溯点被创建,然后第二子句返回 12 。这次与 7 的比较测试成立,因而执行截断。这一截断将删除 qqq中留下的回溯点和指向 ppp 中第二子句的回溯点。

2004.11.1 AI程序设计 66

第三部分:第 12 章 Visual Prolog 程序元素

12.6.15 谓词 finally/2 finally/2 是一个特殊的内部谓词,它以两个子项作为参数。无论第一项的计算结果如何,它确保对第二项的计算。如果满足以下条件,则必然计算第二项: * 第一项成功 * 第一项失败 * 第一项意外终止 整个项表现如下: * 如果第一项成功,那么整个项的表现就像用 "and"连接了两项一样; * 如果第一项失败,则整个项失败(在完成第二项的计算之后); * 如果第一项意外终止,则整个项也将意外终止(在完成第二项的计算之后)。 如果第二项意外终止,则整个项将意外终止。

2004.11.1 AI程序设计 67

第三部分:第 12 章 Visual Prolog 程序元素

12.6.15 谓词 finally/2举例clauses ppp() :- Resource = allocateExternalResource(), finally( use(Resource), deallocateExternalResource(Resource )). 这个例子是 finally/2 谓词的典型应用。不论资源使用的结果如何,外部分配的资源必须被释放。

2004.11.1 AI程序设计 68

第三部分:第 12 章 Visual Prolog 程序元素

12.7 程序段 程序段( Program Sections )用来声明和定义作用域内的实体。section : constantsSection domainsSection predicatesSection constructorsSection factsSection clausesSection conditionalSection 不是所有的段都能在各种作用域中出现。更为详尽的内容请参考接口、类声明和类实现。 条件段在条件编译中介绍。

2004.11.1 AI程序设计 69

第三部分:第 12 章 Visual Prolog 程序元素

第十二章小结 本章介绍 Visual Prolog 的程序元素,内容包括项( Terms )、常量、谓词、子句、事实、运算、程序段等。

第十二章习题1 、 Visual Prolog 程序元素包含那些内容?分析每一项的含义。2 、阅读下面的程序,找出程序中的错误。

2004.11.1 AI程序设计 70

第三部分:第 12 章 Visual Prolog 程序元素

第十二章习题程序如下:PREDICATESlikes_shopping(symbol)has_credit_card(symbol,symbol)bottomed_out(symbol,symbol)CLAUSESlikes_shopping(Who):-has_credit_card(Who,Card),not(bottomed_out(Who,Card)),write(Who," can shop with the ",Card, " credit card.\n").has_credit_card(chris,visa).has_credit_card(chris,diners).has_credit_card(joe,shell).has_credit_card(sam,mastercard).has_credit_card(sam,citibank).bottomed_out(chris,diners).bottomed_out(sam,mastercard).bottomed_out(chris,visa).goallikes_shopping(Who).