电脑桌面
添加蜗牛文库到电脑桌面
安装后可以在桌面快捷访问

Prolog概述

栏目:合同范文发布:2025-01-29浏览:1收藏

Prolog概述

第一篇:Prolog概述

Visual Prolog智能集成开发环境评述

雷英杰

邢清华

孙金萍

张 雷

(空军工程大学导弹学院 陕西三原

713800)

摘 要:Visual Prolog是国际上已经广泛流行的功能强大的通用智能化应用集成开发环境。本文全面评述其功能特点,包括Visual Prolog的由来与发展、可视化开发环境、可视化编程接口、语言与编译器、运行环境等。

关键词:Visual Prolog;人工智能;逻辑程序设计;开发环境

中图分类号:TP393 文献标识码:A 文章编号:1009-3516(2002)05——

Overview of Visual Prolog Intelligent Integrated Development Environment LEI Ying-jie, XING Qing-hua, SUN Jin-ping, ZHANG Lei(The Missile Institute of Air Force Engineering University, Sanyuan Shaanxi, 713800, China)

Abstract Visual Prolog has already become a widely used AI programming language in logic and international developing tools and environment, which is a powerful, general-purpose, intelligent and integrated visual studio.All aspects of Visual Prolog are presented in this paper, such as how it derives and evolves, and its visual programming environment, visual programming interfaces, the features of programming language and compiler, running environment and so on.Keywords: Visual Prolog;Artificial Intelligence;Programming in Logic;Development Environment

智能化是当前计算机、自动化、通信、管理等信息科学技术领域中的新方法、新技术、新产品的重要发展方向与开发策略之一。信息处理的智能化与信息社会对智能的巨大需求是人工智能发展的强大动力。人工智能研究曾取得过许多令人注目的成果,也走过不少弯路,经历过不少挫折。近几年来,随着计算机与网络通信技术的迅猛发展,特别是因特网的大规模普及,人工智能的研究再度活跃起来,并正向更为广阔的领域发展。围绕智能化应用系统的研究和开发也迎来一个蓬勃发展的新时期。因此,引进与消化国际上已经广泛流行的功能强大和通用的智能程序设计语言、工具与环境,对于我国开发智能应用系统十分必要。有鉴于此,本文全面介绍和评述Visual Prolog的功能特点,希望对我国在这一领域从事教学、研究及应用开发的同行有所启迪。

1.Visual Prolog的由来与发展

Prolog语言是人工智能与专家系统领域最著名的逻辑程序设计语言。Visual Prolog意指可视化逻辑程序设计语言,是基于Prolog语言的可视化集成开发环境,是Prolog开发中心(PDC)最新推出的基于Windows环境的智能化编程工具,其语言特性符合相应的国际标准ISO/IEC 13211-1:1995。目前,Visual Prolog在美国、西欧、日本、加拿大、澳大利亚等发达国家和地区十分流行,是国际上研究和开发智能化应用的主流工具之一。预计短时期内,在国际上已经十分流行的最新版本的可视化逻辑程序设计语言Visual Prolog将会在我国广泛流行开来,并将迅速成为我国研究和开发智能化应用的最重要的工具。

Visual Prolog具有模式匹配、递归、回溯、对象机制、事实数据库和谓词库等强大功能[1]。它包含构建大型应用程序所需要的一切特性:图形开发环境、编译器、连接器和调试器,支持模块化和面向对象程序设计,支持系统级编程、文件操作、字符串处理、位级运算、算术与逻辑运算,以及与其它编程语言的接口。Visual Prolog包含一个大型库,捆绑了范围广阔的API函数:包括Windows GUI函数族、ODBC/OCI数据库函数族和Internet函数族(socket、ftp、http、CGI等)。这个开发环境全部使用Visual Prolog语言写成,而且包含对话框、菜单、工具栏等若干编码专家和图形编辑器。Visual Prolog支持Windows 98/Me/NT/2000/XP、OS/2及文本方式下的Linux和SCO UNIX。Visual Prolog非常适合于专家系统、规划和其它AI相关问题的求解,是智能程序设计语言中具有代表性且应用较多的一种程序设计语言。由于这种语言很适合表达人的思维和推理规则,在自然语言理解、机器定理证明、专家系统等方面得到了广泛的应用,在智能程序设计语言中占有相当重要的地位。

Prolog是全世界计算机科学家多年来研究工作的结晶。Prolog的第一个正式版本由法国马赛大学Alain Colmerauer于70年代作为一种逻辑程序设计工具研制。其结果是诞生了一种远比当今Pascal和C这样著名的编程语言功能更加强大的语言。一个特定应用的Prolog程序典型情况下只需要对应C++程序的十分之一程序行。

今天,Prolog是人工智能应用编程和专家系统开发的一个非常重要的工具。更多的“用户友好性”和智能化程序的要求是使Prolog流行起来的另一个原因。但Prolog最重要的好处是非常公平地适用于任何应用领域:通过让程序员建立对象和进程之间的逻辑关系,复杂问题更容易从本质上求解,而且产生的程序在其生命周期更容易维护。定制知识库、专家系统、自然语言接口和智能信息管理系统这些应用都是当前使用Visual Prolog进行程序设计的领域范围[3]。

Prolog已经走出了人工智能实验室,PDC的Visual Prolog是一个商业上富有竞争的通用开发环境。Visual Prolog因其容易增加程序甚至网站的智能化特性而日益变成许多开发者选择的工具。

Prolog是一种众所周知的说明性语言。这就是说,给出所需要的事实和规则,Prolog将使用演绎推理求解编程问题。这与传统的过程性编程语言如C、BASIC和Pascal等形成了鲜明的对照。在过程性语言中,程序员必须提供一步一步的指令,准确地告诉计算机如何求解给定的问题。换句话说,程序员必须预先知道如何求解这个问题。相反,Prolog程序员只需要提供对问题的描述和求解的基本规则。然后,Prolog系统本身将确定如何找到一个解。

由于这种说明性(而不是过程性)方法,众所周知的错误来源,诸如循环操作次数多一次或少一次这样的错误,一开始就被排除了。Prolog鼓励程序员从结构良好的问题描述开始,因而实际上,Prolog也可以被用作指定产品的规格说明工具和实现工具。

Visual Prolog是PDC Prolog和Turbo Prolog的后继产品。在微机上,Visual Prolog是基于Windows环境的,而早期的PDC Prolog和Turbo Prolog是基于DOS环境的。Visual Prolog特别适合于处理知识和知识问题求解,是优秀的智能化应用开发工具。同时它与SQL数据库系统、C++开发系统和其它语言工具如Visual Basic、Borland的Delphi或IBM的Visual Age一样,都致力于同样的目标,已经成为适合于任何应用领域的通用开发工具。当今有一些组织趋向于用数据库技术来求解一切问题,但这种途径常常在开发时间和最终系统的性能两方面导致坏的结果。用Prolog开发的应用程序具有更优越的性能和用户友好性、更短的开发时间。PDC的Visual Prolog特别适用于这些传统类型的数据库任务,因为Visual Prolog具有的编程能力之一就是十分易于使用的数据库引擎。Visual Prolog由于高度优化的编译器,创建的程序非常快,几乎与基于C++的应用程序一样快。

Web编程支持和对象机制这两种功能是对Visual Prolog商用有效性的巨大贡献。对象机制本身就是一种非常强大的建模工具,几乎已经成了Pascal、C++、Smalltalk等语言的一种事实上的标准。Web编程支持是一个重要的新特性。譬如,用Visual Prolog写的专家系统,可以被连接到Web页,在支持部门、网上贸易和其它一些基于Web技术的开发等方面将发挥重要作用。

2.可视化开发环境

Visual Prolog的可视化开发环境(VDE)[4]把编译器与编辑器、资源工具箱、资源和应用程序专家、交互式建造工具和各种浏览工具等结合在一起。

在交互式、可视化地创建用户接口部件之后,就自动生成了一个运行原型。应用程序专家为一个项目创建所需要的全部文件,资源专家知道如何生成Prolog代码,以支持所选择的全部资源。

设计VDE是为了使开发应用程序更加容易、方便和快捷,这些应用程序是基于每一个本地操作系统提供标准接口的高级抽象。功能上相同的可视化开发环境可以运行在所有的Windows平台上。

编码专家(Code Expert)创建并维护Prolog的控件资源代码。Visual Prolog最大的强项可能是把布局设计工具(Layout)和编码专家结合在一起。编码专家完成大部分工作,就是说,你可以在几分钟之内创建一个应用程序,然后从这个原型逐渐增强到最终的应用程序。

应用程序专家(Application Expert)能生成一个新项目,或对一个现有项目进行配置。它说明操作系统、用户接口策略、C编译器、伴随工具等数千种组合情形。当生成一个新项目时,它将自动建立所有的基本工具,如帮助文件、工具栏、菜单等。

资源编辑器包含一组工具,这些工具使得以交互方式可视化地设计和修改用户接口成为可能。可以直接使用鼠标安排控件在对话框或窗口中的布局,设置访问属性。资源由窗口、对话框、位图、图标、光标和字符串等组成,它们是任何使用GUI的应用程序都需要的。Visual Prolog具有很强的引入资源的能力。资源可以从DLL、应用程序、RES文件及其它Visual Prolog项目引入。

Visual Prolog包含一个语言敏感的文本编辑器,它具有现代开发环境中能找到的所有特性。例如编辑器强大的源代码控制功能,可以使Visual Prolog的关键字和其它语言元素的代码有不同的颜色。这些颜色能使谓词名、参数、注释等之间的差别更易于区分。例如,整型常数可以分配以红色显示。编辑器支持不受限制的撤消与重做设施、搜索与替换、剪切、拷贝、粘贴、快速拖放移动块,甚至嵌入超文本链接。此外,与以前的PDC编辑器一样,程序员能够把这个编辑器功能包括在自己的应用程序中。这个编辑器用在VDE中的独有特色是它知道Visual Prolog的所有谓词、用户接口部件、颜色、常量等。所有这些特性都可以容易地用鼠标粘贴到源代码。

VDE包含有帮助生成器。内置的帮助创作系统使得很容易给出应用程序的联机帮助。这个帮助系统是基于PDC的超文本抽象机(HAM)的。在帮助创作系统中,有可能在设计阶段交互式地输入文本,用鼠标标记新的链接,跟随现有的链接。帮助系统能够输出Windows的.RTF格式,所以它可以生成本地的Windows帮助系统。Help编译器(如HCW.EXE)不包括在Visual Prolog中,但可以在Visual C++和Borland C++产品中找到。也可以在ftp.microsoft.com站点下载一个最新版本的Windows帮助编译器。

Visual Prolog编译器为源代码浏览器产生信息,所以,检查模块中的谓词、浏览项目中所有全局谓词,或者查找任何谓词、论域声明或定义的位置,都是可能的。

Visual Prolog也可以使用源代码控制系统,如Visual SourceSafe、PVCS和MKS,因而很容易在几个项目之间共享源代码,也允许多个程序员从事同一项目。

Visual Prolog的联机帮助设施提供一个完全的VDE操作指南和完整的基本Prolog语言及可视化编程方面扩展的参考信息。当Prolog程序较大时,你就会发现Visual Prolog的调试器是一个不可缺少的工具。调试器对编译出来的代码进行工作,允许设置断点和单步执行代码。当单步执行代码时,可以检查变量的值及尚待证实的事实的内容。

Make工具处理编译、连接、资源编辑和资源绑定等所有的复杂性工作。Make工具检查时间邮戳,每次只编译所需要的文件。为了显示项目的结构,可以把项目中的依赖关系显示成一个树。

在Visual Prolog专业版本中有一个VDESRC子目录,在这个子目录中可以找到可视化开发环境的全部源代码。有了它,就可以任意定制所期望的VDE,或者研究在程序中如何实现这些功能、适当的工具和技术。用来安装Visual Prolog的安装程序本身是用Visual Prolog写成的,其核心源代码包含在磁盘上。它能被修改而创建你自己应用程序的安装程序。

3.Visual Prolog语言与编译器

Visual Prolog的编译器产生紧凑的本地代码,足以与Pascal和C编译器所生成的代码媲美。编译器执行几种不同的分析,范围从全局流程分析和确定性机制检查,向下到寄存器分配和偷窥优化[2]。

除了产生有效代码以外,编译器还执行许多高级检查,检测潜在的编译时间问题。主要是类型检验分析、全局流程分析、确定性机制检查和可能的失败检测。

检测编译时间错误的类型检查机制。许多Prolog是无类型的解释程序,而Visual Prolog杰出的特性之一恰恰是其强类型机制,它提供一个额外级别的编程安全性。类型声明是资料性代码,有助于编译器在开发的早期阶段指出创建的类型错误和更严重的逻辑错误。因此,通过比照程序员和开发系统之间的类型,使得类型声明有助于保证一个程序在整个产品生命周期的完整性。更进一步,这些声明帮助编译器生成的程序更有效,在执行期间更节省存储空间。

异常处理和错误陷阱。Visual Prolog包括功能强大的处理错误情况和控制用户中断的机制[1]。程序员可以在错误检查和错误报告的各个级别上进行选择。例如

check_diskette(S):-trap(disk(S), ExitCode, errorhandler(ExitCode)).类和对象。人们常常在面向对象和说明性编程语言之间进行取舍,但在Visual Prolog中,可以同时使用 来自这两种范例的特性。Visual Prolog语言支持对象和类,在设计中与C++实现类似。

可移植性编码。Visual Prolog系统可用于多种平台,并能为多种平台生成程序。除了个别操作系统专用设施和限制之外,Prolog代码在所有平台之间是可移植的。诸如拷贝、重命名和删除文件、调用其它程序、返回日期和时间等函数,在所有平台上的工作都是相同的。应用程序能够针对Windows 98/Me/NT/2000 /XP及Linux等产生各种不同的应用程序版本。

开放式平台。Visual Prolog很好地设计了与其它编程工具的接口。Visual Prolog能生成其它语言可调用的例程,它本身也能够调用其它语言编写的例程。接口是通用的,而且支持所有产生标准.OBJ模块的编译器。此外,Visual Prolog程序还能够调用DLL,并被放在DLL内部。

通过声明全局Visual Prolog谓词为C语言(或stdcall)调用约定,通过声明参数类型和输入/输出流程模式,在Visual Prolog和C之间不用特殊的胶合代码而直接调用C例程(就象它们是Prolog代码一样)是完全可能的。这种接口在两个方向上起作用,当谓词象C语言那样声明时,它们能够被C语言例程直接调用。例如

GLOBAL PREDICATES procedure LONG vpi_LoadDll(STRING)(I)language c procedure LONG vpi_GetDllProc(LONG, STRING ProcName)-(I,I)language c 数据库子系统。快速而非常灵活的数据库子系统使Visual Prolog成为一个比许多4GL数据库应用更适当的选择。这个数据库系统支持一个独特的Visual Prolog项的有序链的集合,而数据库的项可以是语言本身支持的任何抽象或数据结构,从简单的记录到树或图。数据库系统能直接访问单个项,或经由项的链进行回溯,以产生或匹配特定值。项可以存放在三个位置中的任何一处:在一个文件中、在内存中或在EMS中。数据库还支持B+树,以提供快速数据检索和有效改变项排序的能力。

如果你正在LAN应用程序中使用数据库,就可以利用Visual Prolog支持外部数据库系统的文件共享这个优点。通过使用相应的机制,在交易内部使文件访问串行化,从而可有效地提供多用户数据库访问功能。也就是说,一个数据库可以被几个用户或几个进程同时打开。

Prolog解释器源代码。Visual Prolog还包含Prolog推理机PIE(Prolog Inference Engine):用Visual Prolog编写的一个标准Prolog解释器的全部源代码。对于更多地学习Prolog如何工作和如何把元语言能力加进应用程序来说,这个解释程序是一个强大的工具。你可以修改这个解释程序,创建自己专用的逻辑程序设计语言、推理机、专家系统外壳或程序接口等。

把Prolog编译器嵌入到应用程序。在VPITOOLEXAMPBUILD子目录中,有一个例子说明如何把Prolog编译程序和连接程序集成到你的应用程序,以便编译那些尚未工作的规则。通过在.DLL中实现规则,就可以改变规则而不用关闭应用程序。对于规划和调度而言,这是一个非常强大的功能特性。要使用这一特性,必须另付给PDC运行时间费用,签署一个协议,并且不可以用它来创建一个与Visual Prolog产品竞争的应用程序。

4.可视化编程接口

Visual Prolog已经定义了一种可移植的基于GUI的API,称之为可视化编程接口(VPI)[5]。这个API是一种抽象的设施,可以在Windows 98/Me/NT/2000/XP平台中找到。可视化编程接口给Visual Prolog程序员一个比本地编程更可移植和更易使用的GUI API。然而,为了使用户不受限制,VPI也包含不可移植的平台专用设施和选件。如果使用平台专用设施,那么,应用程序就是不可移植的,或者必须使用条件编译提供不同平台的选择行为。还有可能象在WINBIND或PMBIND子目录中那样,直接对潜在的API进行编程。

与基本的可移植VPI一起,许多高级GUI部件已经在VPI顶层实现。这些部件提供了源代码,当然对于VPI所支持的所有平台都是可移植的。这些工具包括删格(Grid)、树型窗口、Explorer视图、工具栏、制表对话框、高级报表处理等。

客户/服务器体系结构。Visual Prolog是一个建造客户/服务器应用程序的功能强大的平台。其主要途径当前是TCP/IP绑定,也可以是Windows下的NETDDE。使用其中任何一种设施,程序员可以在单个机器的多个进程之间,或在网络中分开的各机器程序之间,发送任意复杂的Prolog项。数据库和逻辑服务器可以用这种设施容易地进行建造。

ODBC和可移植SQL绑定。Visual Prolog的外部数据库常常是存储大量数据最快和最灵活的途径。然而,数据可能在另一个数据库系统中已经存在,或者这个应用程序需要与其它仅仅使用特定数据库技术的应用程 序共享数据。在这种情况下,能够连接到外部数据库非常重要,而Visual Prolog与可移植SQL的绑定将使这种情况对于大多数数据库来说得到简化。可移植SQL绑定是基于ODBC、Oracle的OCI库、或DB2的。对Windows平台而言,Visual Prolog还包含更广泛的对Microsoft的ODBC API的直接绑定。

资料处理工具。PDC的DOC工具为处理丰富的格式化资料提供一个高级抽象。用Prolog结构来表示资料使得不受实际格式限制成为可能,而不管它们是否为.RTF、HTML或IPF格式。既有从Prolog项格式到这些格式的生成器,也有分析程序把任何这些格式转换为Prolog项格式。这些工具展现了许多应用程序的能力,如Word资料生成、Internet助理等。这些工具可以在DOC_TOOL子目录中找到,子目录中还包含一个示例程序,可以看到HTML格式的文件如何转换为.RTF格式时的详细情形。

Visual Prolog包含许多特性,可以容易地创建由Internet激活的应用程序,从而提供广泛的Internet编程支持。(1)绑定到套接字(Socket)。包括绑定基本的低级接口和使套接字接口用起来更安全、更容易的高级接口。套接字是针对TCP/IP协议的API,可以用来在Internet的程序之间、在一个网络内部或同一台计算机上的两个进程之间建立一种通信。(2)FTP支持。Visual Prolog包含一些API和示例程序,显示如何使用Internet文件传输协议FTP从Internet服务器发送和接收文件,演示如何使用Internet超文本传输协议(HTTP)。这组API可以用来在Visual Prolog中创建WWW客户与服务器实用程序和Internet代理。(3)CGI支持。Visual Prolog支持CGI,所包含的一些CGI例子演示如何创建和生成动态Web页的Visual Prolog程序。(4)ISAPI支持。Visual Prolog支持Microsoft的ISAPI,允许在Microsoft信息服务器或任何其它支持ISAPI接口的HTTP服务器上有高性能脚本。此外,Visual Prolog还包括一些例子,显示如何使Prolog服务器与Java小程序(Applet)进行通信。

Visual Prolog当前提供一个商业专家系统外壳ESTA,同时还提供了全部源代码,可以定制和包含在自己的应用程序之中。

总之,Visual Prolog提供了大量实例来演示上述各种功能。其中有一个标签专家,是创建和打印标签的一个小应用程序。你将发现它是一个真正展示所包含的VPI工具能力的精致的小例子。此外,随Visual Prolog一起,还提供了许多展示Prolog问题求解的典型例子。

5.Visual Prolog运行环境

推荐的Visual Prolog运行环境如下:MS Windows 98/Me/NT/2000/XP,Pentium以上配置的PC机,推荐64MB RAM以上,硬盘至少75 MB自由空间, 专业版完全安装大约需要200 MB,SVGA以上分辨率的显示器。

参考文献

[1] 雷英杰,张雷,邢清华,孙金萍 编著.Visual Prolog语言教程[M].西安:陕西科技出版社,2002.[2] 雷英杰,邢清华,张雷,孙金萍 编著.Visual Prolog编程指南[M].待出版,2002.[3] H.J.Holst.Visual Prolog Version 5.x Getting Started, Prolog Development Center, 2001.[4] H.J.Holst.Visual Prolog Version 5.x Visual Development Environment, Prolog Development Center, 2001.[5] H.J.Holst.Visual Prolog Version 5.x Visual Programming Interface[M], Prolog Development Center, 2001.雷英杰,男,[1956-],教授,博士生导师。研究领域:人工智能、决策支持、网络与信息安全等。

第二篇:逻辑型程序设计语言PROLOG详细教程

逻辑型程序设计语言PROLOG教程

2.3.1逻辑型程序设计语言PROLOG PROLOG的语句

PROLOG语言只有三种语句,分别称为事实、规则和问题。

1.事实(fact)

格式: <谓词名>(<项表>).功能

一般表示对象的性质或关系。

其中谓词名是以小写英文字母打头的字母、数字、下划线等组成的字符串,项表是以逗号隔开的项序列。

例如:

student(john).like(mary ,music).表示“约翰是学生”和“玛丽喜欢音乐”。2.规则(rule)

格式:

<谓词名>(<项表>):-<谓词名>(<项表>){,<谓词名>(<项表>)}.功能: 一般表示对象间的因果关系、蕴含关系或对应关系。

其中“:-”号表示“if”(也可以直接写为if),其左部的谓词是规则的结论(亦称为头),右部的谓词是规则的前提(亦称为体),{}表示零次或多次重复,逗号表示and(逻辑与),即规则的形式是一个逻辑蕴含式。

例如:

bird(X):-animal(X),has(X,feather).grandfather(X,Y):-father(X,Z),father(Z,Y).第一条规则表示“如果X是动物,并且X有羽毛,则X是鸟”;第二条规则就表示“X是Y的祖父,如果存在Z,X是Z的父亲并且Z又是Y的父亲”。

3.问题(question)

格式: ?-<谓词名>(<项表>){,<谓词名>(<项表>)}.功能 表示用户的询问,它就是程序运行的目标。

例如:

?-student(john).?-like(mary,X).2.3.2 PROLOG程序

PROLOG程序一般由一组事实、规则和问题组成。问题是程序执行的起点,称为程序的目标。

例如下面就是一个PROLOG程序。

likes(bell,sports).likes(mary,music).likes(mary,sports).likes(jane ,smith).friend(john,X):-likes(X,reading),likes(X,music).friend(john,X):-likes(X,sports),likes(X,music).?-friend(john,Y).可以看出,这个程序中有四条事实、两条规则和一个问题。其中事实、规则和问题都分行书写。规则和事实可连续排列在一起,其顺序可随意安排,但同一谓词名的事实或规则必须集中排列在一起。问题不能与规则及事实排在一起,它作为程序的目标要么单独列出,要么在程序运行时临时给出。

这个程序的事实描述了一些对象(包括人和事物)间的关系;而规则则描述了john交朋友的条件,即如果一个人喜欢读书并且喜欢音乐(或者喜欢运动和喜欢音乐),则这个人就是john的朋友(当然,这个规则也可看作是john朋友的定义);程序中的问题是“约翰的朋友是谁?”

当然,PROLOG程序中的目标可以变化,也可以含有多个语句(上例中只有一个)。如果有多个语句,则这些语句称为子目标。例如对上面的程序,其问题也可以是 ?-likes(mary,X).或

?-likes(mary,music).或

?-friend(X,Y).或

?-likes(bell,sports), likes(mary,music), friend(john,X).等等。当然,对于不同的问题,程序运行的结果一般是不一样的。

2.3.3 PROLOG程序的运行机理

PROLOG程序的运行是从目标出发,并不断进行匹配、合一、归结,有时还要回溯,直到目标被完全满足或不能满足时为止。1.自由变量与约束变量

PROLOG中称无值的变量为自由变量,有值的变量为约束变量。一个变量取了某值就说该变量约束于某值,或者说该变量被某值所约束,或者说该变量被某值实例化了。

2.匹配合一

两个谓词可匹配合一,是指两个谓词的名相同,参量项的个数相同,参量类型对应相同,并且对应参量项还满足下列条件之一:

(1)如果两个都是常量,则必须完全相同。

(2)如果两个都是约束变量,则两个约束值必须相同。

(3)如果其中一个是常量,一个是约束变量,则约束值与常量必须相同。

(4)至少有一个是自由变量。例如:下面的两个谓词

pre1(“ob1”,“ob2”,Z)

pre1(“ob1”,X,Y)

只有当变量X被约束为“ob2”,且Y、Z的约束值相同或者至少有一个是自由变量时,它们才是匹配合一的。

3.回溯

所谓回溯,就是在程序运行期间,当某一个子目标不能满足(即谓词匹配失败)时,控制就返回到前一个已经满足的子目标(如果存在的话),并撤消其有关变量的约束值,然后再使其重新满足。成功后,再继续满足原子目标。如果失败的子目标前再无子目标,则控制就返回到该子目标的上一级目标(即该子目标谓词所在规则的头部)使它重新匹配。回溯也是PROLOG的一个重要机制。

下面,我们介绍PROLOG程序的运行过程。我们仍以上面的程序为例。设所给的询问是

?-friend(john,Y).(john和谁是朋友?)

则求解目标为

friend(john,Y).这时,系统对程序进行扫描,寻找能与目标谓词匹配合一的事实或规则头部。显然,程序中前面的四条事实均不能与目标匹配,而第五个语句的左端即规则

friend(john,X):-likes(X,reading),likes(X,music).的头部可与目标谓词匹配合一。但由于这个语句又是一个规则,所以其结论要成立则必须其前提全部成立。于是,对原目标的求解就转化为对新目标

likes(X,reading),likes(X,music).的求解。这实际是经归结,规则头部被消去,而目标子句变为

?-likes(X,reading),likes(X,music).现在依次对子目标

likes(X,reading)和likes(X,music)

求解。

子目标的求解过程与主目标完全一样,也是从头对程序进行扫描,不断进行测试和匹配合一等,直到匹配成功或扫描完整个程序为止。可以看出,对第一个子目标like(X,reading)的求解因无可匹配的事实和规则而立即失败,进而导致规则

friend(john,X):-likes(X,reading),likes(X,music).的整体失败。于是,刚才的子目标

likes(X,reading)和likes(X,music)

被撤消,系统又回溯到原目标friend(john,X)。这时,系统从该目标刚才的匹配语句处(即第五句)向下继续扫描程序中的子句,试图重新使原目标匹配,结果发现第六条语句的左部,即规则

friend(john,X):-likes(X,sports),likes(X,music).的头部可与目标为谓词匹配。但由于这个语句又是一个规则,于是,这时对原目标的求解,就又转化为依次对子目标

likes(X,sports)和likes(X,music) 的求解。这次子目标likes(X,sports)与程序中的事实立即匹配成功,且变量X被约束为bell。于是,系统便接着求解第二个子目标。由于变量X已被约束,所以这时第二个子目标实际上已变成了

likes(bell,music).由于程序中不存在事实likes(bell,music),所以该目标的求解失败。于是,系统就放弃这个子目标,并使变量X恢复为自由变量,然后回溯到第一个子目标,重新对它进行求解。由于系统已经记住了刚才已同第一子目标谓词匹配过的事实的位置,所以重新求解时,便从下一个事实开始测试。

易见,当测试到程序中第三个事实时,第一个子目标便求解成功,且变量X被约束为mary。这样,第二个子目标也就变成了

likes(mary,music).再对它进行求解。这次很快成功。

由于两个子目标都求解成功,所以,原目标friend(john,Y)也成功,且变量Y被约束为mary(由Y与X的合一关系)。于是,系统回答:

Y=mary

程序运行结束。

上面只给出了问题的一个解。如果需要和可能的话,系统还可把john的所有朋友都找出来。我们把上述程序的运行过程再用示意图(图2─1)描述如下:

图2─1

PROLOG程序运行机理示例

上述程序的运行是一个通过推理实现的求值过程。我们也可以使它变为证明过程。例如,把上述程序中的询问改为

friend(john,mary)

则系统会回答:yes

若将询问改为:

riend(john,smith)

则系统会回答:no

从上述程序的运行过程可以看出,PROLOG程序的执行过程是一个(归结)演绎推理过程。其特点是:推理方式为反向推理,控制策略是深度优先,且有回溯机制。其具体实现方法是:匹配子句的顺序是自上而下;子目标选择顺序是从左向右;(归结后)产生的新子目标总是插入被消去的目标处(即目标队列的左部)。PROLOG的这种归结演绎方法被称为SLD(LinearresolutionwithSelectionfunctionforDefiniteclause)归结,或SLD反驳-消解法。SLD归结就是PROLOG程序的运行机理,它也就是所谓的PROLOG语言的过程性语义。2.4

Turbo PROLOG程序设计 2.4.1 Turbo PROLOG的程序结构

一个完整的Turbo PROLOG(2.0版)程序一般包括常量段、领域段、数据库段、谓词段、目标段和子句段等六个部分。各段以其相应的关键字constants、domains、database、predicates、goal和clauses开头加以标识。:

另外,在程序的首部还可以设置指示编译程序执行特定任务的编译指令;在程序的任何位置都可设置注解。总之,一个完整的TurboPROLOG(2.0版)程序的结构如下

/*<注释>*/

<编译指令>

constants

<常量说明>

domains

<域说明>

database

<数据库说明>

predicates

<谓词说明>

goal

<目标语句>

clauses

<子句集>

当然,一个程序不一定要包括上述所有段,但一个程序至少要有一个predicates段、clauses段和goal段。在大多数情形中,还需要一个domains段,以说明表、复合结构及用户自定义的域名。如若省略goal段,则可在程序运行时临时给出,但这仅当在开发环境中运行程序时方可给出。若要生成一个独立的可执行文件,则在程序中必须包含goal段。另一方面,一个程序也只能有一个goal段。

例2.3 如果把上节中的程序要作为TurboPROLOG程序,则应改写为:

/*例子程序-1*/

DOMAINS

name=symbol

PREDICATES

likes(name,name).friend(name,name)

GOAL

friend(john,Y),write(″Y=″,Y).CLAUSES likes(bell,sports).likes(mary,music).likes(mary,sports).likes(jane,smith).friend(john,X):-likes(X,sports),likes(X,music).friend(john,X):-likes(X,reading),likes(X,music).结合上例,我们再对上述程序结构中的几个主要段的内容和作用加以说明(其余段在后面用到时再作说明):

领域段该段说明程序谓词中所有参量项所属的领域。领域的说明可能会出现多层说明,直到最终说明到Turbo PROLOG的标准领域为止(如上例所示)。Turbo PROLOG的标准领域即标准数据类型,包括整数、实数、符号、串和符号等,其具体说明如表2.1所示。

表2.1 Turbo PROLOG的标准领域

谓词段:该段说明程序中用到的谓词的名和参量项的名(但Turbo PROLOG的内部谓词无须说明)。

子句段:该段是Turbo PROLOG程序的核心,程序中的所有事实和规则就放在这里,系统在试图满足程序的目标时就对它们进行操作。

目标段:该段是放置程序目标的地方。目标段可以只有一个目标谓词,例如上面的例子中就只有一个目标谓词;也可以含有多个目标谓词,如:

goal

readint(X),Y=X+3,write(“Y=”,Y).就有三个目标谓词。这种目标称为复合目标。

另外,一般称程序目标段中的目标为内部目标,而称在程序运行时临时给出的目标为外部目标。

2.4.2 Turbo PROLOG的数据与表达式 1.领域

1)标准领域

Turbo PROLOG中不定义变量的类型,只说明谓词中各个项的取值域。2)结构

结构也称复合对象,它是TurboPROLOG谓词中的一种特殊的参量项(类似于谓词逻辑中的函数)。

结构的一般形式为

<函子>(<参量表>)

其中函子及参量的标识符与谓词相同。注意,这意味着结构中还可包含结构。所以,复合对象可表达树形数据结构。例如下面的谓词

likes(Tom,sports(football,basketball,table-tennis)).中的

sports(football,basketball,table-tennis)

就是一个结构,即复合对象。

又如:

person(“张华”,student(“西安石油学院”),address(“中国”,“陕西”,“西安”)).reading(“王宏”,book(“人工智能技术基础教程”,“西安电子科技大学出版社”)).friend(father(“Li”),father(“Zhao”)).这几个谓词中都有复合对象。复合对象在程序中的说明,需分层进行。例如,对于上面的谓词

likes(Tom,sports(football,basketball,table-tennis)).在程序中可说明如下:

domains

name=symbol

sy=symbol

sp=sports(sy,sy,sy)

predicates

likes(name,sp)3)表

表的一般形式是

[x1,x2,…,xn]

其中xi(i=1,2,…,n)为PROLOG的项,一般要求同一个表的元素必须属于同一领域。

不含任何元素的表称为空表,记为[]。例如下面就是一些合法的表。

[1,2,3]

[apple,orange,banana,grape,cane]

[“PROLOG”,“MAENS”,“PROGRAMMING”,“in logic”] [[a,b],[c,d],[e]] []

表的最大特点是其元素个数可在程序运行期间动态变化。表的元素也可以是结构或表,且这时其元素可以属于不同领域。

例如:

name(“Li Ming”),age(20),sex(male),address(xi an)] [[1,2],[3,4,5],[6,7]]

都是合法的表。后一个例子说明,表也可以嵌套。

实际上,表是一种特殊的结构。它是递归结构的另一种表达形式。这个结构的函数名取决于具体的PROLOG版本。这里我们就用一个圆点来表示。

下面就是一些这样的结构及它们的表表示形式:

结构形式

表形式 ·(a,[])

[a] ·(a,·(b,[]))

[a,b] ·(a,·(b,·(c,[])))

[a,b,c]

表的说明方法是在其组成元素的说明符后加一个星号*。如:

domains

lists=string*

predicates

pl(lists)

就说明谓词pl中的项lists是一个由串string组成的表。

对于由结构组成的表,至少得分三步说明。例如对于下面谓词p中的表

p([name(“Liming”),age(20)])

则需这样说明:

domains

rec=seg*

seg=name(string);age(integer)

predicates

p(rec)2.常量与变量

由上面的领域可知,Turbo PROLOG的常量有整数、实数、字符、串、符号、结构、表和文件这八种数据类型。同理,Turbo PROLOG的变量也就有这八种取值。另外,变量名要求必须是以大写字母或下划线开头的字母、数字和下划线序列,或者只有一个下划线。这后一种变量称为无名变量。3.算术表达式

Turbo PROLOG提供了五种最基本的算术运算:加、减、乘、除和取模,相应运算符号为+、-、*、/、mod。这五种运算的顺序为:*、/、mod优先于+、-。同级从左到右按顺序运算,括号优先。算术表达式的形式与数学中的形式基本一样。例如:

数学中的算术表达式

PROLOG中的算术表达式

x+yz

X+Y*Z

ab-c/d

A*B-C/D

u mod v

U mod V(表示求U除以V所得的余数)即是说,Turbo PROLOG中算术表达式采用通常数学中使用的中缀形式。这种算术表达式为PROLOG的一种异体结构,若以PROLOG的结构形式来表示,则它们应为

+(X,*(Y,Z))

-(*(A,B),/(C,D))

mod(U,V)

所以,运算符+、-、*、/和mod实际也就是PROLOG内部定义好了的函数符。

在Turbo PROLOG程序中,如果一个算术表达式中的变元全部被实例化(即被约束)的话,则这个算术表达式的值就会被求出。求出的值可用来实例化某变量,也可用来同其他数量进行比较,用一个算术表达式的值实例化一个变量的方法是用谓词“is”或“=”来实现。例如:

Y is X+5 或 Y=X+5

(*)

就使变量Y实例化为X+5的值(当然X也必须经已被某值实例化),可以看出,这里对变量Y的实例化方法类似于其他高级程序语言中的“赋值”,但又不同于赋值。例如,在PROLOG中下面的式子是错误的:

X=X+1 4.关系表达式

Turbo PROLOG提供了六种常用的关系运算,即小于、小于或等于、等于、大于、大于或等于和不等于,其运算符依次为

<,<=,=,>,>=,<>

Turbo PROLOG的关系表达式的形式和数学中的也基本一样,例如:

数学中的关系式

Turbo PROLOG中的关系式

X+1≥Y

X+1>=Y

X≠Y

X<>Y 即是说,Turbo PROLOG中的关系式也用中缀形式。当然,这种关系式为Turbo PROLOG中的异体原子。若按Turbo PROLOG中的原子形式来表示,则上面的两个例子为

>=(X+1,Y)和<>(X,Y)

所以上述六种关系运算符,实际上也就是Turbo PROLOG内部定义好了的六个谓词。这六个关系运算符可用来比较两个算术表达式的大小。

例如:

brother(Name1,Name2):-person(Name1,man,Age1),person(Name2,man,Age2),mother(Z,Name1),mother(Z,Name2),Age1>Age2.需要说明的是,“=”的用法比较特殊,它既可以表示比较,也可以表示约束值,即使在同一个规则中的同一个“=”也是如此。

例如:

(例一)

p(X,Y,Z):-Z=X+Y.当变量X、Y、Z全部被实例化时,“=”就是比较符。如:对于问题

Goal:p(3,5,8).机器回答:yes。而对于

Goal:p(3,5,7).机器回答:no。

即这时机器把X+Y的值,与Z的值进行比较。

(例二)但当X,Y被实例化,为Z未被实例化时,“=”号就是约束符。如:

Goal:p(3,5,Z).机器回答:Z=8 这时,机器使Z实例化为X+Y的结果。2.4.3 输入与输出

虽然PROLOG能自动输出目标子句中的变量的值,但这种输出功能必定有限,往往不能满足实际需要;另一方面,对通常大多数的程序来说,运行时从键盘上输入有关数据或信息也是必不可少的。为此每种具体PROLOG一般都提供专门的输入和输出谓词,供用户直接调用。例如,下面就是TorboPROLOG的几种输入输出谓词:

(1)readln(X)。

这个谓词的功能是从键盘上读取一个字符串,然后约束给变量X。

(2)readint(X)。

这个谓词的功能是从键盘上读取一个整数,然后约束给变量X,如果键盘上打入的不是整数则该谓词失败。

(3)readreal(X)。

这个谓词的功能是从键盘上读取一个实数,然后约束给变量X,如果键盘上打入的不是实数则该谓词失败。

(4)readchar(X)。

这个谓词的功能是从键盘上读取一个字符,然后约束给变量X,如果键盘上打入的不是单个字符,则该谓词失败。

(5)write(X1,X2,… Xn)。

这个谓词的功能是把项Xi(i=1,2,…n)的值显示在屏幕上或者打印在纸上,当有某个Xi未实例化时,该谓词失败,其中的Xi可以是变量,也可以是字符串或数字。

(6)nl换行谓词。它使后面的输出(如果有的话)另起一行。另外,利用write的输出项“n”也同样可起换行作用。例如:

write(“name”), n l ,write(“age”)

与write(“name”,“n”,“age”)的效果完全一样。例2.4用上面的输入输出谓词编写一个简单的学生成绩数据库查询程序。

PREDICATES student(integer,string,real)grade GOAL grade.CLAUSES student(1,“张三”,90.2).student(2,“李四”,95.5).student(3,“王五”,96.4).grade:-write(“请输入姓名:”),readln(Name), student(-,Name,Score), nl,write(Name,“的成绩是”,Score).grade:-write(“对不起,找不到这个学生!”).grade:-write(“对不起,找不到这个学生!”).下面是程序运行时的屏幕显示: 请输入姓名: 王五

王五的成绩是96.4。

2.4.4 分支与循环

PROLOG中并无专门的分支和循环语句,但PROLOG也可实现分支和循环程序结构。1.分支

对于通常的IF-THEN-ELSE分支结构,PROLOG可用两条同头的并列规则实现。例如,将

IF x>0THENx:=1

ELSE x:=0 用PROLOG实现则是 Br :-x>0,x=1.Br :-x=0.类似地,对于多分支,可以用多条规则实现。例如: Br :-x>0,x=1.Br :-x=0,x=0.Br :-x<0,x=-1.2.循环

PROLOG可以实现计循环次数的FOR循环,也可以实现不计循环次数的DO循环。例如下面的程序段就实现了循环,它使得write语句重复执行了三次,而打印输出了三个学生的记录。

student(1,“张三”,90.2).student(2,“李四”,95.5).student(3,“王五”,96.4).print:-student(Number,Name,Score),write(Number,Name,Score),n l ,Number=3.这个例子可以看作是计数循环。当然,也可以通过设置计数器而实现真正的计数循环。下面的程序段实现的则是不计数的DO循环。

student(1,“张三”,90.2).student(2,“李四”,95.5).student(3,“王五”,96.4).print:-student(Number,Name,Score),write(Number,Name,Score),nl,fail.print:-.这个程序段中的fail是一个内部谓词,它的语义是恒失败。这个程序段与上面的程序段的差别仅在于把原来用计数器(或标记数)循环控制语句变成了恒失败谓词fail,另外再增加了一个print语句。增加这个语句的目的是为程序设置一个出口。因为fail是恒失败,下面若无出口的话,将引起print本身的失败。进而又会导致程序中的连锁失败。

2.4.5 动态数据库

动态数据库就是在内存中实现的动态数据结构。它由事实组成,程序可以对它操作,所以在程序运行期间它可以动态变化。Turbo PROLOG提供了三个动态数据库操作谓词: asserta().assertz().retract().其中fact表示事实。这三个谓词的功能是:

asserta().把fact插入当前动态数据库中的同名谓词的事实之前;

assertz().把fact插入当前动态数据库中的同名谓词的事实之后;

retract().把fact从当前动态数据库中删除。

例如语句

asserta(student(20,“李明”,90.5)).将在内存的谓词名为student的事实前插入一个新事实:

student(20,“李明”,90.5)

如果内存中还没有这样的事实,则它就是第一个。又如语句

retract(student(20,-,-)).将从内存的动态数据库中的删除事实

student(20,-,-)它可解释为学号为20的一个学生的记录。注意,这里用了无名变量-。

可以看出,PROLOG提供的动态数据库机制,可非常方便地实现堆栈、队列等动态数据结构,提供的数据库操作谓词大大简化了编程。

另外,PROLOG还提供了谓词

save().consult().前者可将当前的动态数据库存入磁盘文件,后者则可将磁盘上的一个事实数据文件调入内存。

2.4.6 表处理与递归

表是PROLOG中一种非常有用的数据结构。表的表述能力很强,数字中的序列、集合,通常语言中的数组、记录等均可用表来表示。表的最大特点是其长度不固定,在程序的运行过程中可动态地变化。具体来讲,就是在程序运行时,可对表施行一些操作,如给表中添加一个元素,或从中删除一个元素,或者将两个表合并为一个表等等。用表还可以方便地构造堆栈、队列、链表、树等动态数据结构。

表还有一个重要特点,就是它可分为头和尾两部分。表头是表中第一个元素,而表尾是表中除第一个元素外的其余元素按原来顺序组成的表。例如下面的例子:

表头

表尾

[1,2,3,4,5]

[2,3,4,5]

[apple,orange,banana]

apple

[orange,banana]

[[a,b],[c],[d,e]]

[a,b]

[[c],[d,e]]

[“PROLOG”]

“PROLOG“

[]

[]

无定义

无定义

在程序中是用竖线“|”来区分表头和表尾的,而且还可以使用变量。例如一般地用[H|T]来表示一个表,其中H、T都是变量,H为表头,T为表尾。注意,此处H是一个元素(表中第一个元素),而T则是一个表(除第一个元素外的表中其余元素按原来顺序组成的表)。表的这种表示法很有用,它为表的操作提供了极大的方便。下面我们就给出用这种表示法通过匹配合一提取表头和表尾的例子。

表1

表2

合一后的变量值 [X|Y]

[a,b,c]

X=a,Y=[b,c] [X|Y]

[a]

X=a,Y=[] [a|Y]

[X,b]

X=a,Y=[b] [X,Y,Z]

[a,b,c]

X=a,Y=b,Z=c [[a,Y]|Z]

[[X,b],[c]]

X=a,Y=b,Z=[[c]]

还需说明的是,表中的竖杠“|”后面只能有一个变量。例如写法[X|Y,Z]就是错误的。但竖杠的前面的变量可以多于一个。例如写法[X,Y|Z]是允许的。这样,这个表同[a,b,c]匹配合一后,有

X=a,Y=b,Z=[c]

另外,竖杠的前面和后面也可以是常量,例如[a|Y]和[X|b]都是允许的,但注意,后一个表称为无尾表,如果它同表[a|Y]匹配,则有

X=a,Y=b

(而不是Y=[b])

如果无竖杠“|”,则不能分离出表尾。例如,表[X,Y,Z]与[a,b,c]合一后得X=a,Y=b,Z=c。其中变量Z并非等于[c]。

例2.5 设计一个能判断对象X是表L的成员的程序。

我们可以这样设想:

(1)如果X与表L中的第一个元素(即表头)是同一个对象,则X就是L的成员;

(2)如果X是L的尾部的成员,则X也就是L的成员。

根据这种逻辑关系,于是有下面的PROLOG程序:

member(X,[X|Tail]).member(X,[Head|Tail]):-member(X,Tail).

其中第一个子句的语义就是上面的第一句话,第二个子句的语义就是上面的第二句话。可以看出,这个程序中使用了递归技术,因为谓词member的定义中又含有它自身。利用这个程序我们就可以判定任意给定的一个对象和一个表之间是否具有member(成员)关系。

例如,我们取表L为[a,b,c,d],取X为a,对上面的程序提出如下询问:

Goal:member(a,[a,b,c,d]).

则有回答:yes

同样对于询问:

Goal:member(b,[a,b,c,d]).Goal:member(c,[a,b,c,d]).Goal:member(d,[a,b,c,d]).

都有回答:yes

但若询问

Goal:member(e,[a,b,c,d]).

则回答:no

如果我们这样询问

Goal:member(X,[a,b,c,d]).

意思是要证明存在这样的X,它是该表的成员,这时系统返回X的值,即

X=a

如果需要的话,系统还会给出X的其他所有值。

例2.6 表的拼接程序,即把两个表连接成一个表。

append([],L,L).append([H|T],L2,[H|Tn]):-append(T,L2,Tn).

程序中第一个子句的意思是空表同任一表L拼接的结果仍为表L;第二个子句的意思是说,一个非空的表L1与另一个表L2拼接的结果L3是这样一个表,它的头是L1的头,它的尾是由L1的尾T同L2拼接的结果Tn。这个程序刻划了两个表与它们的拼接表之间的逻辑关系。

可以看出,谓词append是递归定义的,子句append([],L,L).为终结条件,即递归出口。

对于这个程序,如果我们询问

Goal:append([1,2,3],[4,5],L).

则系统便三次递归调用程序中的第二个子句,最后从第一个子句终止,然后反向依次求出每次的拼接表,最后输出

L=[1,2,3,4,5]

当然,对于这个程序也可以给出其他各种询问,如: Goal:append([1,2,3],[4,5],[1,2,3,4,5]). 系统回答:yes Goal:append([1,2,3],[4,5],[1,2,3,4,5,6]). 系统回答:no Goal:append([1,2,3],Y,[1,2,3,4,5]). 系统回答:Y=[4,5]

Goal:append(X,[4,5],[1,2,3,4,5]).

系统回答:X=[1,2,3]

Goal:append(X,Y,[1,2,3,4,5]). 系统回答: X=[],Y=[1,2,3,4,5] X=[1],Y=[2,3,4,5] X=[1,2],Y=[3,4,5] X=[1,2,3],Y=[4,5] …

等等(如果需要所有解的话)。

例2.7 表的输出。

print([]).print([H|T]):-write(H),print(T).例2.8 表的倒置,即求一个表的逆序表。

reverse([],[]).reverse([H|T],L):-reverse(T,L1),append(L1,[H],L).

这里,reverse的第一个项是输入,即原表,第二个项是输出,即原表的倒置。2.4.7 回溯控制

PROLOG在搜索目标解的过程中,具有回溯机制,即当某一个子目标Gi不能满足时,就返回到该子目标的前一个子目标Gi-1,并放弃Gi-1的当前约束值,使它重新匹配合一。在实际问题中,有时却不需要回溯,为此PROLOG中就专门定义了一个阻止回溯的内部谓词——“!”,称为截断谓词。

截断谓词的语法格式很简单,就是一个感叹号“!”。!的语义是:

(1)若将“!”插在子句体内作为一个子目标,它总是立即成功;

(2)若“!”位于子句体的最后,则它就阻止对它所在子句的头谓词的所有子句的回溯访问,而让回溯跳过该头谓词(子目标),去访问前一个子目标(如果有的话);

(3)若“!”位于其他位置,则当其后发生回溯且回溯到“!”处时,就在此处失败,并且“!”还使它所在子句的头谓词(子目标)整个失败(即阻止再去访问头谓词的其余子句(如果有的话),即迫使系统直接回溯到该头谓词(子目标)的前一个子目标(如果有的话))。

例2.9考虑下面的程序:

p(a).(2─1)

p(b).(2─2)

q(b).(2─3)

r(X):-p(X),q(X).(2─4)

r(c).

对于目标:r(Y).

可有一个解

Y=b

但当我们把式(2─4)改为

r(X):-p(X),!,q(X).(2─4′)

时,却无解。

这是由于添加了截断谓词“!”。因为式(2─4′)中求解子目标p(X)时,X被约束到a,然后跳过“!”,但在求解子目标q(a)时遇到麻烦,于是又回溯到“!”,而“!”阻止了对p(X)的下一个子句p(b)和r的下一个定义子句r(c)的访问。从而,导致整个求解失败。

例2.10 设有程序:

g0:-g11,g12,g13.(2─5)g0:-g14.(2─6)g12:-g21,!,g23.(2─7)g12:-g24,g25.(2─8).........

给出目标:g0.假设运行到子目标g23时失败,这时如果子句(2─7)中无!的话,则会回溯到g21,并且,如果g21也失败的话,则会访问下面的子句(2─8)。但由于有!存在,所以不能回溯到g21,而直接宣告g12失败。于是,由子句(2─5),这时则回溯到g11。如果我们把子句(2─7)改为

g12:-g21, g23,!.(2─9)当然这时若g23失败时,便可回溯到g21,而当g21也失败时,便回溯到g12,即子句(2─8)被“激活”。但对于修改后的程序,如果g13失败,则虽然可回溯到g12,但对g12不做任何事情,便立即跳过它,而回溯到g11,如果子句(2─9)中无!,则当g13失败时,回溯到g12便去考虑子句(2─8),只有当子句(2─8)再失败时才回溯到g11。2.4.8 程序举例

下面我们给出几个简单而又典型的例子程序。通过这些程序,读者可以进一步体会和理解PROLOG程序的风格和能力,也可以掌握一些基本的编程技巧。

例2.11 下面是一个简单的路径查询程序。程序中的事实描述了如图2─2所示的有向图,规则是图中两节点间有通路的定义。

图2─2

有向图

predicates

road(symbol,symbol)

path(symbol,symbol)clauses

road(a,b).road(a,c).road(b,d).road(c,d).road(d,e).road(b,e).path(X,Y):-road(X,Y).path(X,Y):-road(X,Z),path(Z,Y).程序中未含目标,所以运行时需给出外部目标。例如当给目标:

path(a,e).

系统将回答:yes

但当给目标:

path(e,a).

时,系统则回答:no

如果给出目标:

run. 且在程序中增加子句

run:-path(a,X),write(”X=“,X),nl,fail.run. 屏幕上将会输出:

X=b

X=c

X=d

X=e

X=d

X=e

X=e

即从a出发到其他节点的全部路径。

例2.12 下面是一个求阶乘程序,程序中使用了递归。

/*aFactorialProgram*/

domains

n,f=integer

predicates

factorial(n,f)

goal

readint(I),factorial(I,F),write(I,”!=“,F).clauses

factorial(1,1).factorial(N,Res):-

N>0,N1=N-1,factorial(N1,FacN1),Res=N*FacN1.程序运行时,从键盘输入一个整数,屏幕上将显示其阶乘数。

例2.13 下面是一个表的排序程序,采用插入排序法。

/*insertsort*/

domains

listi=integer*

predicates

insert-sort(listi , listi)

insert(integer,listi,listi)

asc-order(integer,integer)

clauses

insert-sort([],[]).insert-sort([H|Tail],Sorted-list):-insert-sort(Tail,Sorted-Tail),insert(H,Sorted-Tail,Sorted-list).insert(X,[Y|Sorted-list],[Y|Sorted-list1]):-asc-order(X,Y),!,insert(X,Sorted-list,Sorted-list1).insert(X,Sorted-list,[X|Sorted-list]).asc-order(X,Y):-X>Y.程序中对表处理使用了递归。程序中也未给出目标,需要在运行时临时给出。例如当给目标:

insert-sort([5,3,4,2,6,1,7,8,9,0],L).系统将输出:

L=[0,1,2,3,4,5,6,7,8,9]

例2.14下面是一个简单的通信录管理程序,其中用到输入输出、动态数据库等。通过阅读这个程序,我们还可以掌握循环结构和简单的菜单程序编写方法。

/*通信录*/

database

person(string,integer)

predicates

address-book

chose(integer)

input query

repeat goal

address-book.clauses

address-book:-repeat,clearwindow,write(”==============“),nl,write(”1--录入“),nl, write(”2--查询“),nl, write(”3--退出“),nl,write(”==============“),nl, write(”请选择:-“), readint(N), chose(N).chose(1):-input,fail.chose(2):-query,fail.chose(3):-clearwindow,!.input:-clearwindow,write(”姓名:“),readln(Name),write(”电话:“),readint(Tel),assertz(person(Name,Tel)),!.query:-clearwindow,write(”姓名:“),readln(Name),person(Name,Tel),write(”电话:“,Tel),readchar(-),!.repeat.repeat:-repeat.程序中的repeat恒成功。它与内部谓词fail配合实现了循环。

需说明的是,这仅是一个演示性程序,还不能实用。因为这里的通信录并未存入磁盘文件。用谓词save就可方便地把通信录存入磁盘文件。例如用语句

save(”addrbook.dat“)

就可把已插入内存的person事实存入文件addrbook.dat中。而语句

consult(”addrbook.dat“)

则可又将该文件中的事实装入内存。

2.4 函数型程序设计语言LISP

LISP语言的主要特点是:

(1)LISP程序由一组函数组成,程序的执行过程是函数的调用过程。

(2)程序和数据在形式上是相同的,即都是符号表达式,简称为S─表达式。

(3)递归是LISP语言的主要控制结构。

(4)程序以交互方式运行。

2.2.1 LISP的程序结构与运行机制

LISP的程序一般由函数的定义和函数的调用两部分组成。其一般格式为:

(DEFUN(<函数名>(<形参表>)<函数体>)

(<函数名>(〖WB〗<形参表>)<函数体>)

(<函数名>(<形参表>)<函数体>))

(<函数名><实参表>)

(<函数名><实参表>)

(<函数名><实参表>)

其中的“DEFUN”是定义函数的关键字,“函数名”可以是系统的内部函数(名),也可以是用户用DEFUN定义的函数(名)。例如下面就是一个LISP程序。

(DEFUNHANOI(a b c n)

(COND((=n1)(MOVE-DISK a c))

(T(HANOI a c b(-n1))

(MOVE-DISK a c)

(HANOI b a c(-n1))))(DEFUNMOVE-DISK(from to)(TERPRI)(PRINC″Move Disk From″)(PRINC from)(PRINC”To“)(PRINC to))

(HANOI′a′b′c3)2.2.2 S─表达式

从语法上看,LISP程序的基本单位是S─表达式。S─表达式又可分为原子和表两大类。原子(atom)是由字母和数字组成的字符串,是S─表达式的最简单情况。原子又可分为文字原子、串原子和数字原子三种。

文字原子又称符号(symbol),是以字母开头的字母数字串,用来表示常量、变量和函数的名字等。例如:ABC、X1等。

串原子是由双引号括起来的一串字符。如”LISP Program"。

数字原子由数字串组成。在其前面可以有符号“-”或“+”,中间可出现“.”,用来表示整数和实数。例如:256、-66、3.14159等。

S─表达式可以递归定义如下:

(1)原子是S─表达式。

(2)若S1和S2是S─表达式,则(S1·S2)也是S─表达式。由定义,下面的式子都是S─表达式:

X2

123

(A·B)

(A·(B·C))

表(list)是LISP语言中最常用的数据类型,也是主要的处理对象。表是由圆括号括起来的由空格分开的若干个元素的集合。

表的一般形式为:

()

例如:

(X Y Z),(+12),(A(B C))

左括号后面的第一个元素称为表头,其余的元素组成的表称为表尾。例如,对于表

(+12)的头为+,尾为(12)。

特别地,元素个数为零的表为空表,记为()或NIL。

表是一种特殊的S─表达式,每一个表都对应着一个S─表达式。二者的关系由下面的例子说明。

表←——————————————→S-表达式

(A)

(A·NIL)

(AB)

(A·(B·NIL))

(ABC)

(A·(B·(C·NIL)))

((AB)CD)

((A·(B·NIL))·(C·(D·NIL)))

可以看出,表的S─表达式的结构实际是一棵二叉树。

2.2.3 基本函数

LISP的函数都以表的形式出现,并一律使用前缀表示方式,即表头为函数名,并且每个函数都有一个返回值。LISP的函数可分为语言自身提供的内部函数(称为基本函数或系统函数)和用户自定义函数两类。基本函数的种类有十多个,下面仅给出其中主要的几类。

1.表处理函数

表处理是LISP的主要特色,表处理的函数也很多,下面仅给出最常用的几个。1)CAR函数

格式(CAR<表>)

其中CAR为函数名,它是一个保留字(下同)。功能取出表中的表头。

例如:(CAR′(LISP Language Program))返回值为:LISP 2)CDR函数

格式(CDR<表>)

功能取出表

Prolog概述

第一篇:Prolog概述 Visual Prolog智能集成开发环境评述 雷英杰 邢清华 孙金萍 张 雷 (空军...
点击下载
分享:
上一篇:文摘概述下一篇:建筑构造专题概述
最新文档
热门文章
    确认删除?
    QQ
    • QQ点击这里给我发消息
    微信客服
    • 微信客服
    回到顶部