Prolog - 快速指南


Prolog - 简介

Prolog 顾名思义,是逻辑编程的缩写形式。它是一种逻辑性和声明性编程语言。在深入了解 Prolog 的概念之前,让我们首先了解什么是逻辑编程。

逻辑编程是计算机编程范式之一,其中程序语句表达形式逻辑系统中不同问题的事实和规则。在这里,规则以逻辑子句的形式编写,其中存在头部和主体。例如,H是头部,B1、B2、B3是身体的元素。现在,如果我们说“当 B1、B2、B3 都为真时,H 为真”,这就是一条规则。另一方面,事实就像规则一样,但没有任何实体。因此,事实的一个例子是“H 为真”。

一些逻辑编程语言,如 Datalog 或 ASP(答案集编程)被称为纯声明性语言。这些语言允许声明程序应该完成什么。没有关于如何执行任务的分步说明。然而,其他语言(例如 Prolog)具有声明性和命令性属性。这还可能包括诸如“为了解决问题 H,执行 B1、B2 和 B3”之类的程序语句。

下面给出了一些逻辑编程语言 -

  • ALF(代数逻辑函数式编程语言)。

  • ASP(答案集编程)

  • 环化L

  • 数据记录

  • 模糊CLIPS

  • 雅努斯

  • 帕洛格

  • 序言

  • 序言++

  • 罗普

逻辑和函数式编程

我们将讨论逻辑编程和传统函数式编程语言之间的差异。我们可以使用下图来说明这两个 -

逻辑和函数式编程

从这个例子中,我们可以看到,在函数式编程中,我们必须定义过程,以及过程如何工作的规则。这些程序根据算法逐步解决一个特定问题。另一方面,对于逻辑编程,我们将提供知识库。使用这个知识库,机器可以找到给定问题的答案,这与函数式编程完全不同。

在函数式编程中,我们必须提及如何解决一个问题,但在逻辑编程中,我们必须指定我们真正想要解决哪个问题。然后逻辑编程会自动找到合适的解决方案来帮助我们解决该特定问题。

现在让我们看看下面更多的差异 -

函数式编程 逻辑编程
函数式编程遵循冯·诺依曼架构,或者使用顺序步骤。 逻辑编程使用抽象模型,或处理对象及其关系。
语法实际上是语句序列,如 (a, s, I)。 语法基本上是逻辑公式(Horn Clauses)。
计算通过顺序执行语句来进行。 它通过扣除子句来计算。
逻辑和控制混合在一起。 逻辑和控制可以分开。

什么是 Prolog?

Prolog 或LOG ics 中的PRO语法是一种逻辑和声明性编程语言。它是支持声明式编程范式的第四代语言的主要示例之一。这特别适合涉及符号或非数字计算的程序。这是使用 Prolog 作为人工智能编程语言的主要原因,其中符号操作推理操作是基本任务。

在Prolog中,我们不需要提及一个问题如何解决,我们只需要提及问题是什么,Prolog就会自动解决它。然而,在Prolog中我们应该给出线索作为解决方法

Prolog 语言基本上具有三个不同的元素 -

事实- 事实是真实的谓词,例如,如果我们说“汤姆是杰克的儿子”,那么这是一个事实。

规则- 规则是包含条件子句的事实的消除。为了满足规则,应该满足这些条件。例如,如果我们将规则定义为 -

grandfather(X, Y) :- father(X, Z), parent(Z, Y)

这意味着,对于 X 来说,Y 是 Y 的祖父,Z 应该是 Y 的父母,X 应该是 Z 的父亲。

问题- 要运行序言程序,我们需要一些问题,这些问题可以通过给定的事实和规则来回答。

Prolog 的历史

Prolog 的遗产包括对定理证明器的研究以及 20 世纪 60 年代和 1970 年代开发的其他一些自动演绎系统。Prolog 的推理机制基于 1965 年提出的 Robinson 解析原理和 Green (1968) 的答案提取机制。随着线性解析程序的出现,这些想法有力地结合在一起。

明确的目标导向线性解析程序推动了通用逻辑编程系统的发展。第一个Prolog 是基于Colmerauer在 1970 年工作的Marseille Prolog。这个 Marseille Prolog 解释器的手册(Roussel,1975)是对 Prolog 语言的第一个详细描述。

Prolog 也被认为是支持声明式编程范式的第四代编程语言。1981 年宣布的著名的日本第五代计算机计划采用 Prolog 作为开发语言,从而引起了对该语言及其功能的广泛关注。

Prolog的一些应用

Prolog 用于各个领域。它在自动化系统中起着至关重要的作用。以下是使用 Prolog 的其他一些重要领域 -

  • 智能数据库检索

  • 自然语言理解

  • 规范语言

  • 机器学习

  • 机器人规划

  • 自动化系统

  • 解决问题

Prolog - 环境设置

在本章中,我们将讨论如何在我们的系统中安装 Prolog。

序言版本

在本教程中,我们使用 GNU Prolog,版本:1.4.5

官方网站

这是 GNU Prolog 官方网站,在这里我们可以看到有关 GNU Prolog 的所有必要详细信息,还可以获取下载链接。

http://www.gprolog.org/

直接下载链接

下面给出的是 Windows 版 GNU Prolog 的直接下载链接。对于 Mac 或 Linux 等其他操作系统,您可以通过访问官方网站获取下载链接(上面给出了链接) -

http://www.gprolog.org/setup-gprolog-1.4.5-mingw-x86.exe(32位系统)

http://www.gprolog.org/setup-gprolog-1.4.5-mingw-x64.exe(64位系统)

安装指南

  • 下载exe文件并运行它。

  • 您将看到如下所示的窗口,然后单击下一步-

窗户

选择要安装软件的正确目录,否则就安装在默认目录中。然后点击下一步

选择目录

您将看到以下屏幕,只需转到下一步

选择开始菜单文件夹

您可以验证以下屏幕,并选中/取消选中相应的框,否则您可以将其保留为默认值。然后点击下一步

选择附加任务

在下一步中,您将看到以下屏幕,然后单击“安装”

安装

然后等待安装过程完成。

安装中

最后单击Finish启动 GNU Prolog。

设置向导

GNU prolog 已成功安装,如下所示 -

安装成功

Prolog - 你好世界

在上一节中,我们已经了解了如何安装 GNU Prolog。现在,我们将了解如何在 Prolog 环境中编写一个简单的Hello World程序。

你好世界计划

运行GNU prolog后,我们可以直接从控制台编写hello world程序。为此,我们必须编写如下命令 -

write('Hello World').

注意- 在每一行之后,您必须使用一个句点 (.) 符号来表明该行已结束。

相应的输出如下所示 -

你好世界

现在让我们看看如何在 Prolog 控制台中运行 Prolog 脚本文件(扩展名为 *.pl)。

在运行 *.pl 文件之前,我们必须将该文件存储到 GNU prolog 控制台指向的目录中,否则只需通过以下步骤更改目录 -

步骤 1 - 从 prolog 控制台,转到文件 > 更改目录,然后单击该菜单。

步骤 2 - 选择正确的文件夹并按OK

选择工作目录

现在我们可以在prolog控制台中看到,它表明我们已经成功更改了目录。

序言_控制台

步骤 3 - 现在创建一个文件(扩展名为 *.pl)并编写代码如下 -

main :- write('This is sample Prolog program'),
write(' This program is written into hello_world.pl file').

现在让我们运行代码。要运行它,我们必须按如下方式编写文件名 -

[hello_world]

输出如下 -

序言控制台1

Prolog - 基础知识

在本章中,我们将获得一些关于 Prolog 的基本知识。所以我们将继续 Prolog 编程的第一步。

本章将涵盖的不同主题是 -

知识库- 这是逻辑编程的基本部分之一。我们将详细了解知识库,以及它如何帮助逻辑编程。

事实、规则和查询- 这些是逻辑编程的构建块。我们将获得一些有关事实和规则的详细知识,并且还将看到一些将在逻辑编程中使用的查询。

在这里,我们将讨论逻辑编程的基本构建块。这些构建块是事实、规则和查询

事实

我们可以将事实定义为对象之间的显式关系以及这些对象可能具有的属性。所以事实本质上是无条件真实的。假设我们有一些事实如下 -

  • 汤姆是一只猫

  • 库纳尔喜欢吃面食

  • 头发是黑色的

  • 纳瓦兹喜欢玩游戏

  • 普拉秋莎很懒。

所以这些是一些绝对真实的事实。这些实际上是陈述,我们必须将其视为真实的。

以下是编写事实的一些准则 -

  • 属性/关系的名称以小写字母开头。

  • 关系名称显示为第一项。

  • 对象显示为括号内以逗号分隔的参数。

  • 一段时间 ”。” 必须结束一个事实。

  • 对象也以小写字母开头。它们也可以以数字开头(如 1234),也可以是用引号引起来的字符串,例如 color(penink, 'red')。

  • 电话(agnibha,1122334455)。也称为谓词或子句。

句法

事实的语法如下 -

relation(object1,object2...).

例子

以下是上述概念的示例 -

cat(tom).
loves_to_eat(kunal,pasta).
of_color(hair,black).
loves_to_play_games(nawaz).
lazy(pratyusha).

规则

我们可以将规则定义为对象之间的隐式关系。所以事实是有条件的。因此,当一个关联条件为真时,谓词也为真。假设我们有一些规则如下 -

  • 莉莉跳舞会很高兴。

  • 如果汤姆正在寻找食物,他就饿了。

  • 如果杰克和比利都喜欢打板球,他们就是朋友。

  • 如果学校放假,他就会去玩,而且他有空。

这些是一些有条件为真的规则,因此当右侧为真时,左侧也为真。

这里符号(:-)将发音为“If”,或“隐含于”。这也称为颈部符号,该符号的左侧称为头部,右侧称为身体。这里我们可以使用逗号(,),称为合取,也可以使用分号,称为析取。

句法

rule_name(object1, object2, ...) :- fact/rule(object1,
 object2, ...)
Suppose a clause is like :
P :- Q;R.
This can also be written as
P :- Q.
P :- R.

If one clause is like :
P :- Q,R;S,T,U.

Is understood as
P :- (Q,R);(S,T,U).
Or can also be written as:
P :- Q,R.
P :- S,T,U.

例子

happy(lili) :- dances(lili).
hungry(tom) :- search_for_food(tom).
friends(jack, bili) :- lovesCricket(jack), lovesCricket(bili).
goToPlay(ryan) :- isClosed(school), free(ryan).

查询

查询是关于对象和对象属性之间关系的一些问题。所以问题可以是任何问题,如下所示 -

  • 汤姆是猫吗?

  • Kunal 喜欢吃面食吗?

  • 莉莉幸福吗?

  • 瑞恩会去玩吗?

因此,根据这些查询,逻辑编程语言可以找到答案并返回它们。

逻辑编程知识库

在本节中,我们将了解逻辑编程中的知识库是什么。

嗯,众所周知,逻辑编程由三个主要组成部分——事实、规则查询。这三者如果我们把事实和规则作为一个整体收集起来,那就形成了一个知识库。所以我们可以说知识库事实和规则的集合

现在,我们将看到如何编写一些知识库。假设我们有第一个知识库,称为 KB1。在 KB1 中,我们有一些事实。事实用于陈述在感兴趣的领域中无条件真实的事物。

知识库1

假设我们有一些知识,Priya、Tiyasha 和 Jaya 是三个女孩,其中 Priya 会做饭。让我们尝试以更通用的方式来写这些事实,如下所示 -

girl(priya).
girl(tiyasha).
girl(jaya).
can_cook(priya).

注意- 这里我们用小写字母写名称,因为在 Prolog 中,以大写字母开头的字符串表示变量

现在我们可以通过提出一些查询来使用这个知识库。“Priya 是女孩吗?”,它会回答“是”,“Jamini 是女孩吗?” 那么它会回答“否”,因为它不知道jamini是谁。我们的下一个问题是“Priya 可以做饭吗?”,它会说“是”,但如果我们问 Jaya 同样的问题,它会说“不”。

输出

GNU Prolog 1.4.5 (64 bits)
Compiled Jul 14 2018, 13:19:42 with x86_64-w64-mingw32-gcc
By Daniel Diaz
Copyright (C) 1999-2018 Daniel Diaz
| ?- change_directory('D:/TP Prolog/Sample_Codes').

yes
| ?- [kb1]
.
compiling D:/TP Prolog/Sample_Codes/kb1.pl for byte code...
D:/TP Prolog/Sample_Codes/kb1.pl compiled, 3 lines read - 489 bytes written, 10 ms

yes
| ?- girl(priya)
.

yes
| ?- girl(jamini).

no
| ?- can_cook(priya).

yes
| ?- can_cook(jaya).

no
| ?-

让我们看看另一个知识库,其中有一些规则。规则包含一些关于感兴趣的域有条件为真的信息。假设我们的知识库如下 -

sing_a_song(ananya).
listens_to_music(rohit).

listens_to_music(ananya) :- sing_a_song(ananya).
happy(ananya) :- sing_a_song(ananya).
happy(rohit) :- listens_to_music(rohit).
playes_guitar(rohit) :- listens_to_music(rohit).

所以上面给出了一些事实和规则。前两条是事实,其余的是规则。我们知道阿纳尼亚会唱歌,这意味着她也听音乐。因此,如果我们问“Ananya 听音乐吗?”,答案是正确的。同样,“罗希特快乐吗?”,这也是真的,因为他听音乐。但如果我们的问题是“Ananya 会弹吉他吗?”,那么根据知识库,它会说“No”。这些是基于此知识库的查询的一些示例。

输出

| ?- [kb2].
compiling D:/TP Prolog/Sample_Codes/kb2.pl for byte code...
D:/TP Prolog/Sample_Codes/kb2.pl compiled, 6 lines read - 1066 bytes written, 15 ms

yes
| ?- happy(rohit).

yes
| ?- sing_a_song(rohit).

no
| ?- sing_a_song(ananya).

yes
| ?- playes_guitar(rohit).

yes
| ?- playes_guitar(ananya).

no
| ?- listens_to_music(ananya).

yes
| ?-

知识库3

知识库3的事实和规则如下:

can_cook(priya).
can_cook(jaya).
can_cook(tiyasha).

likes(priya,jaya) :- can_cook(jaya).
likes(priya,tiyasha) :- can_cook(tiyasha).

假设我们想查看会做饭的成员,我们可以在查询中使用一个变量。变量应以大写字母开头。结果会一一显示。如果我们按回车键,那么它就会出来,否则如果我们按分号(;),那么它就会显示下一个结果。

让我们看一个实际的演示输出,以了解它是如何工作的。

输出

| ?- [kb3].
compiling D:/TP Prolog/Sample_Codes/kb3.pl for byte code...
D:/TP Prolog/Sample_Codes/kb3.pl compiled, 5 lines read - 737 bytes written, 22 ms
warning: D:/TP Prolog/Sample_Codes/kb3.pl:1: redefining procedure can_cook/1
         D:/TP Prolog/Sample_Codes/kb1.pl:4: previous definition
         
yes
| ?- can_cook(X).

X = priya ? ;

X = jaya ? ;

X = tiyasha

yes
| ?- likes(priya,X).

X = jaya ? ;

X = tiyasha

yes
| ?-

Prolog - 关系

关系是我们在 Prolog 中必须适当提及的主要特征之一。这些关系可以表达为事实和规则。之后我们将了解家庭关系,如何在 Prolog 中表达基于家庭的关系,以及家庭的递归关系。

我们将通过创建事实和规则来创建知识库,并对它们进行查询。

Prolog 中的关系

在Prolog程序中,它指定了对象和对象的属性之间的关系。

假设有一条语句“Amit 有一辆自行车”,那么我们实际上是在声明两个对象之间的所有权关系——一个是 Amit,另一个是自行车。

如果我们问一个问题,“阿米特有自行车吗?”,我们实际上是在试图找出一种关系。

关系有很多种,其中有些也可以是规则。即使关系没有明确定义为事实,规则也可以找出关系。

我们可以定义兄弟关系如下 -

两个人是兄弟,如果,

  • 他们都是男性。

  • 他们有同一个父母。

现在考虑我们有以下短语 -

  • 父母(sudip,piyus)。

  • 父母(sudip,raj)。

  • 男(piyus)。

  • 男(拉吉)。

  • 兄弟(X,Y):-父母(Z,X),父母(Z,Y),男性(X),男性(Y)

这些子句可以给我们答案:piyus 和 raj 是兄弟,但这里我们将得到三对输出。它们是:(piyus,piyus),(piyus,raj),(raj,raj)。对于这些对,给定的条件是正确的,但是对于(piyus,piyus),(raj,raj)这对,他们实际上不是兄弟,他们是同一个人。因此,我们必须正确地创建子句才能形成关系。

修改后的关系如下 -

A 和 B 是兄弟,如果 -

  • A和B均为男性

  • 他们有同一个父亲

  • 他们有同一个母亲

  • A和B不一样

Prolog 中的家庭关系

在这里我们将看到家庭关系。这是可以使用 Prolog 形成复杂关系的示例。我们想要制作一个家谱,并将其映射为事实和规则,然后我们可以对它们运行一些查询。

假设家谱如下 -

家庭关系

从这棵树上,我们可以了解到,关系很少。这里鲍勃是帕姆和汤姆的孩子,鲍勃还有两个孩子——安和帕特。鲍勃有一个兄弟莉兹,他的父母也是汤姆。所以我们想要做出如下谓词 -

谓词

  • 父母(帕姆,鲍勃)。

  • 父母(汤姆,鲍勃)。

  • 父母(汤姆,莉兹)。

  • 父母(鲍勃,安)。

  • 父母(鲍勃,帕特)。

  • 父母(帕特,吉姆)。

  • 父母(鲍勃,彼得)。

  • 父母(彼得,吉姆)。

从我们的例子中,它有助于说明一些重要的观点 -

  • 我们根据家谱中给定的信息声明对象的 n 元组来定义父关系。

  • 用户可以轻松地向Prolog系统查询程序中定义的关系。

  • Prolog 程序由以句号终止的子句组成。

  • 关系的参数可以(除其他外)是:具体对象,或常量(例如 pat 和 jim),或一般对象,例如 X 和 Y。我们程序中的第一类对象称为Atomics。第二类对象称为变量。

  • 对系统的问题包含一个或多个目标。

有些事实可以用两种不同的方式来写,例如家庭成员的性别可以用任何一种形式来写 -

  • 女性(帕姆)。

  • 男(汤姆)。

  • 男(鲍勃)。

  • 女(莉兹)。

  • 女(拍拍)。

  • 女(安)。

  • 男(吉姆)。

或者采用下面的形式 -

  • 性别(pam,女性)。

  • 性别(汤姆,男性)。

  • 性别(鲍勃,男性)。

  • … 等等。

现在,如果我们想建立母亲和姐妹的关系,那么我们可以写如下 -

母亲和姐妹的关系

在 Prolog 语法中,我们可以写 -

  • 母亲(X,Y):-父母(X,Y),女性(X)。

  • 姐妹(X,Y):- 父母(Z,X),父母(Z,Y),女性(X),X \== Y。

现在让我们看看实际演示 -

知识库 (family.pl)

female(pam).
female(liz).
female(pat).
female(ann).
male(jim).
male(bob).
male(tom).
male(peter).
parent(pam,bob).
parent(tom,bob).
parent(tom,liz).
parent(bob,ann).
parent(bob,pat).
parent(pat,jim).
parent(bob,peter).
parent(peter,jim).
mother(X,Y):- parent(X,Y),female(X).
father(X,Y):- parent(X,Y),male(X).
haschild(X):- parent(X,_).
sister(X,Y):- parent(Z,X),parent(Z,Y),female(X),X\==Y.
brother(X,Y):-parent(Z,X),parent(Z,Y),male(X),X\==Y.

输出

| ?- [family].
compiling D:/TP Prolog/Sample_Codes/family.pl for byte code...
D:/TP Prolog/Sample_Codes/family.pl compiled, 23 lines read - 3088 bytes written, 9 ms

yes
| ?- parent(X,jim).

X = pat ? ;

X = peter

yes
| ?-
mother(X,Y).

X = pam
Y = bob ? ;

X = pat
Y = jim ? ;

no
| ?- haschild(X).

X = pam ? ;

X = tom ? ;

X = tom ? ;

X = bob ? ;

X = bob ? ;

X = pat ? ;

X = bob ? ;

X = peter

yes
| ?- sister(X,Y).

X = liz
Y = bob ? ;

X = ann
Y = pat ? ;

X = ann
Y = peter ? ;

X = pat
Y = ann ? ;

X = pat
Y = peter ? ;

(16 ms) no
| ?-

现在让我们看看我们可以从以前的家庭关系中建立更多的关系。因此,如果我们想建立祖父母关系,可以如下形成 -

祖父母

我们还可以创建一些其他关系,如妻子、叔叔等。我们可以将这些关系写成如下 -

  • 祖父母(X,Y):- 父母(X,Z),父母(Z,Y)。

  • 祖母(X,Z):-母亲(X,Y),父母(Y,Z)。

  • 祖父(X,Z):-父亲(X,Y),父母(Y,Z)。

  • 妻子(X,Y):-父母(X,Z),父母(Y,Z),女性(X),男性(Y)。

  • 叔叔(X,Z):-兄弟(X,Y),父母(Y,Z)。

那么让我们编写一个 prolog 程序来看看它的实际效果。在这里我们还将看到跟踪执行情况的跟踪。

知识库 (family_ext.pl)

female(pam).
female(liz).
female(pat).
female(ann).

male(jim).
male(bob).
male(tom).
male(peter).

parent(pam,bob).
parent(tom,bob).
parent(tom,liz).
parent(bob,ann).

parent(bob,pat).
parent(pat,jim).
parent(bob,peter).
parent(peter,jim).

mother(X,Y):- parent(X,Y),female(X).
father(X,Y):-parent(X,Y),male(X).
sister(X,Y):-parent(Z,X),parent(Z,Y),female(X),X\==Y.
brother(X,Y):-parent(Z,X),parent(Z,Y),male(X),X\==Y.
grandparent(X,Y):-parent(X,Z),parent(Z,Y).
grandmother(X,Z):-mother(X,Y),parent(Y,Z).
grandfather(X,Z):-father(X,Y),parent(Y,Z).
wife(X,Y):-parent(X,Z),parent(Y,Z),female(X),male(Y).
uncle(X,Z):-brother(X,Y),parent(Y,Z).

输出

| ?- [family_ext].
compiling D:/TP Prolog/Sample_Codes/family_ext.pl for byte code...
D:/TP Prolog/Sample_Codes/family_ext.pl compiled, 27 lines read - 4646 bytes written, 10 ms

| ?- uncle(X,Y).

X = peter
Y = jim ? ;

no
| ?- grandparent(X,Y).

X = pam
Y = ann ? ;

X = pam
Y = pat ? ;

X = pam
Y = peter ? ;

X = tom
Y = ann ? ;

X = tom
Y = pat ? ;

X = tom
Y = peter ? ;

X = bob
Y = jim ? ;

X = bob
Y = jim ? ;

no
| ?- wife(X,Y).

X = pam
Y = tom ? ;

X = pat
Y = peter ? ;

(15 ms) no
| ?-

跟踪输出

在 Prolog 中我们可以跟踪执行情况。要跟踪输出,您必须输入“trace.”进入跟踪模式。然后从输出中我们可以看到我们只是在追踪“pam is mother of who?”。通过将 X = pam 和 Y 作为变量来查看跟踪输出,其中 Y 将是 bob 作为答案。要退出跟踪模式,请按“notrace”。

程序

| ?- [family_ext].
compiling D:/TP Prolog/Sample_Codes/family_ext.pl for byte code...
D:/TP Prolog/Sample_Codes/family_ext.pl compiled, 27 lines read - 4646 bytes written, 10 ms

(16 ms) yes
| ?- mother(X,Y).

X = pam
Y = bob ? ;

X = pat
Y = jim ? ;

no
| ?- trace.
The debugger will first creep -- showing everything (trace)

yes
{trace}
| ?- mother(pam,Y).
   1 1 Call: mother(pam,_23) ?
   2 2 Call: parent(pam,_23) ?
   2 2 Exit: parent(pam,bob) ?
   3 2 Call: female(pam) ?
   3 2 Exit: female(pam) ?
   1 1 Exit: mother(pam,bob) ?
   
Y = bob

(16 ms) yes
{trace}
| ?- notrace.
The debugger is switched off

yes
| ?-

家庭关系中的递归

在上一节中,我们已经看到我们可以定义一些家庭关系。这些关系本质上是静态的。我们还可以创建一些递归关系,可以用下图来表达 -

家庭关系中的递归 家庭关系中的递归1

所以我们可以理解,前驱关系是递归的。我们可以使用以下语法来表达这种关系 -

predecessor(X, Z) :- parent(X, Z).
predecessor(X, Z) :- parent(X, Y),predecessor(Y, Z).

现在让我们看看实际演示。

知识库 (family_rec.pl)

female(pam).
female(liz).
female(pat).
female(ann).

male(jim).
male(bob).
male(tom).
male(peter).

parent(pam,bob).
parent(tom,bob).
parent(tom,liz).
parent(bob,ann).
parent(bob,pat).
parent(pat,jim).
parent(bob,peter).
parent(peter,jim).

predecessor(X, Z) :- parent(X, Z).
predecessor(X, Z) :- parent(X, Y),predecessor(Y, Z).

输出

| ?- [family_rec].
compiling D:/TP Prolog/Sample_Codes/family_rec.pl for byte code...
D:/TP Prolog/Sample_Codes/family_rec.pl compiled, 21 lines read - 1851 bytes written, 14 ms

yes
| ?- predecessor(peter,X).

X = jim ? ;

no
| ?- trace.
The debugger will first creep -- showing everything (trace)

yes
{trace}
| ?- predecessor(bob,X).
   1 1 Call: predecessor(bob,_23) ?
   2 2 Call: parent(bob,_23) ?
   2 2 Exit: parent(bob,ann) ?
   1 1 Exit: predecessor(bob,ann) ?
   
X = ann ? ;
   1 1 Redo: predecessor(bob,ann) ?
   2 2 Redo: parent(bob,ann) ?
   2 2 Exit: parent(bob,pat) ?
   1 1 Exit: predecessor(bob,pat) ?
   
X = pat ? ;
   1 1 Redo: predecessor(bob,pat) ?
   2 2 Redo: parent(bob,pat) ?
   2 2 Exit: parent(bob,peter) ?
   1 1 Exit: predecessor(bob,peter) ?
   
X = peter ? ;
   1 1 Redo: predecessor(bob,peter) ?
   2 2 Call: parent(bob,_92) ?
   2 2 Exit: parent(bob,ann) ?
   3 2 Call: predecessor(ann,_23) ?
   4 3 Call: parent(ann,_23) ?
   4 3 Fail: parent(ann,_23) ?
   4 3 Call: parent(ann,_141) ?
   4 3 Fail: parent(ann,_129) ?
   3 2 Fail: predecessor(ann,_23) ?
   2 2 Redo: parent(bob,ann) ?
   2 2 Exit: parent(bob,pat) ?
   3 2 Call: predecessor(pat,_23) ?
   4 3 Call: parent(pat,_23) ?
   4 3 Exit: parent(pat,jim) ?
   3 2 Exit: predecessor(pat,jim) ?
   1 1 Exit: predecessor(bob,jim) ?
   
X = jim ? ;
   1 1 Redo: predecessor(bob,jim) ?
   3 2 Redo: predecessor(pat,jim) ?
   4 3 Call: parent(pat,_141) ?
   4 3 Exit: parent(pat,jim) ?
   5 3 Call: predecessor(jim,_23) ?
   6 4 Call: parent(jim,_23) ?
   6 4 Fail: parent(jim,_23) ?
   6 4 Call: parent(jim,_190) ?
   6 4 Fail: parent(jim,_178) ?
   5 3 Fail: predecessor(jim,_23) ?
   3 2 Fail: predecessor(pat,_23) ?
   2 2 Redo: parent(bob,pat) ?
   2 2 Exit: parent(bob,peter) ?
   3 2 Call: predecessor(peter,_23) ?
   4 3 Call: parent(peter,_23) ?
   4 3 Exit: parent(peter,jim) ?
   3 2 Exit: predecessor(peter,jim) ?
   1 1 Exit: predecessor(bob,jim) ?
   
X = jim ?

(78 ms) yes
{trace}
| ?-

Prolog - 数据对象

在本章中,我们将学习 Prolog 中的数据对象。它们可以分为几个不同的类别,如下所示 -

数据对象

以下是不同类型数据对象的一些示例 -

  • Atomics - tom、pat、x100、x_45

  • 数字 - 100、1235、2000.45

  • 变量 - X、Y、Xval、_X

  • 结构 - 天(9, jun, 2017), 点(10, 25)

Atomics和变量

在本节中,我们将讨论 Prolog 的Atomics、数字和变量。

Atomics

Atomics是常数的一种变体。它们可以是任何名称或对象。当我们尝试使用 Atom 时,应该遵循一些规则,如下所示 -

由字母、数字和下划线字符“_”组成的字符串,以小写字母开头。例如 -

  • 阿扎哈尔

  • b59

  • b_59

  • b_59AB

  • b_x25

  • 安塔拉_萨卡

特殊字符的字符串

我们必须记住,在使用这种形式的Atomics时,需要小心,因为某些特殊字符字符串已经具有预定义的含义;例如 ':-'。

  • <--->

  • =======>

  • ...

  • .:.

  • ::=

用单引号括起来的字符串。

如果我们想要一个以大写字母开头的Atomics,这很有用。通过将其括在引号中,我们可以将其与变量区分开来 -

  • '鲁拜'

  • “阿林达姆_查特吉”

  • “苏米特·米特拉”

数字

常数的另一个变体是数字。所以整数可以表示为100、4、-81、1202。在Prolog中,整数的正常范围是从-16383到16383。

Prolog 也支持实数,但通常 Prolog 程序中浮点数的用例很少,因为 Prolog 用于符号、非数字计算。实数的处理取决于Prolog的实现。实数示例有 3.14159、-0.00062、450.18 等。

这些变量位于“简单对象”部分下。正如我们之前所看到的,在我们的 Prolog 程序中,变量可以在许多这样的情况下使用。所以Prolog中定义变量有一些规则。

我们可以定义Prolog变量,变量是由字母、数字和下划线字符组成的字符串。它们以大写字母或下划线字符开头。变量的一些例子是 -

  • X

  • 会员名称

  • 学生名单

  • 购物清单

  • _a50

  • _15

Prolog 中的匿名变量

匿名变量没有名称。prolog 中的匿名变量由单个下划线字符“_”编写。一件重要的事情是每个单独的匿名变量都被视为不同的。它们不一样。

现在的问题是,我们应该在哪里使用这些匿名变量呢?

假设我们的知识库中有一些事实——“吉姆讨厌汤姆”,“帕特讨厌鲍勃”。因此,如果汤姆想找出谁讨厌他,那么他可以使用变量。但是,如果他想检查是否有人讨厌他,我们可以使用匿名变量。所以当我们想要使用变量,但又不想泄露变量的值时,那么我们可以使用匿名变量。

那么让我们看看它的实际实现 -

知识库 (var_anonymous.pl)

hates(jim,tom).
hates(pat,bob).
hates(dog,fox).
hates(peter,tom).

输出

| ?- [var_anonymous].
compiling D:/TP Prolog/Sample_Codes/var_anonymous.pl for byte code...
D:/TP Prolog/Sample_Codes/var_anonymous.pl compiled, 3 lines read - 536 bytes written, 16 ms

yes
| ?- hates(X,tom).

X = jim ? ;

X = peter

yes
| ?- hates(_,tom).

true ? ;

(16 ms) yes
| ?- hates(_,pat).

no
| ?- hates(_,fox).

true ? ;

no
| ?-

Prolog - 运算符

在下面的部分中,我们将了解 Prolog 中有哪些不同类型的运算符。比较运算符和算术运算符的类型。

我们还将看到它们与任何其他高级语言运算符有何不同,它们在语法上有何不同,以及它们在工作中有何不同。我们还将看到一些实际演示来了解不同运算符的用法。

比较运算符

比较运算符用于比较两个方程或状态。以下是不同的比较运算符 -

操作员 意义
X > Y X 大于 Y
X < Y X 小于 Y
X >= Y X 大于或等于 Y
X =< Y X 小于或等于 Y
X =:= Y X 和 Y 值相等
X =\= Y X 和 Y 值不相等

您可以看到“=<”运算符、“=:=”运算符和“=\=”运算符在语法上与其他语言不同。让我们看一些实际的演示。

例子

| ?- 1+2=:=2+1.

yes
| ?- 1+2=2+1.

no
| ?- 1+A=B+2.

A = 2
B = 1

yes
| ?- 5<10.

yes
| ?- 5>10.

no
| ?- 10=\=100.

yes

这里我们可以看到 1+2=:=2+1 返回 true,但 1+2=2+1 返回 false。这是因为,第一种情况是检查 1 + 2 的值是否与 2 + 1 相同,另一种情况是检查两个模式 '1+2' 和 '2+1' 是否相同或不是。由于它们不相同,因此返回 no (false)。在 1+A=B+2 的情况下,A 和 B 是两个变量,它们会被自动分配给一些与模式匹配的值。

Prolog 中的算术运算符

算术运算符用于执行算术运算。有几种不同类型的算术运算符,如下所示 -

操作员 意义
+ 添加
- 减法
* 乘法
/ 分配
** 力量
// 整数除法
模组 模数

让我们看一段实用的代码来了解这些运算符的用法。

程序

calc :- X is 100 + 200,write('100 + 200 is '),write(X),nl,
        Y is 400 - 150,write('400 - 150 is '),write(Y),nl,
        Z is 10 * 300,write('10 * 300 is '),write(Z),nl,
        A is 100 / 30,write('100 / 30 is '),write(A),nl,
        B is 100 // 30,write('100 // 30 is '),write(B),nl,
        C is 100 ** 2,write('100 ** 2 is '),write(C),nl,
        D is 100 mod 30,write('100 mod 30 is '),write(D),nl.

注意- nl 用于创建新行。

输出

| ?- change_directory('D:/TP Prolog/Sample_Codes').

yes
| ?- [op_arith].
compiling D:/TP Prolog/Sample_Codes/op_arith.pl for byte code...
D:/TP Prolog/Sample_Codes/op_arith.pl compiled, 6 lines read - 2390 bytes written, 11 ms

yes
| ?- calc.
100 + 200 is 300
400 - 150 is 250
10 * 300 is 3000
100 / 30 is 3.3333333333333335
100 // 30 is 3
100 ** 2 is 10000.0
100 mod 30 is 10

yes
| ?-

Prolog - 循环和决策

在本章中,我们将讨论 Prolog 中的循环和决策。

循环

循环语句用于多次执行代码块。一般来说,for、while、do-while 是编程语言(如 Java、C、C++)中的循环结构。

使用递归谓词逻辑多次执行代码块。其他一些语言中没有直接循环,但我们可以使用几种不同的技术来模拟循环。

程序

count_to_10(10) :- write(10),nl.
count_to_10(X) :-
   write(X),nl,
   Y is X + 1,
   count_to_10(Y).

输出

| ?- [loop].
compiling D:/TP Prolog/Sample_Codes/loop.pl for byte code...
D:/TP Prolog/Sample_Codes/loop.pl compiled, 4 lines read - 751 bytes written, 16 ms

(16 ms) yes
| ?- count_to_10(3).
3
4
5
6
7
8
9
10

true ?
yes
| ?-

现在创建一个采用最低和最高值的循环。因此,我们可以使用 Between() 来模拟循环。

程序

让我们看一个示例程序 -

count_down(L, H) :-
   between(L, H, Y),
   Z is H - Y,
   write(Z), nl.
   
count_up(L, H) :-
   between(L, H, Y),
   Z is L + Y,
   write(Z), nl.

输出

| ?- [loop].
compiling D:/TP Prolog/Sample_Codes/loop.pl for byte code...
D:/TP Prolog/Sample_Codes/loop.pl compiled, 14 lines read - 1700 bytes written, 16 ms

yes
| ?- count_down(12,17).
5

true ? ;
4

true ? ;
3

true ? ;
2

true ? ;
1

true ? ;
0

yes
| ?- count_up(5,12).
10

true ? ;
11

true ? ;
12

true ? ;
13

true ? ;
14

true ? ;
15

true ? ;
16

true ? ;
17

yes
| ?-

决策

决策语句是 If-Then-Else 语句。因此,当我们尝试匹配某些条件并执行某些任务时,我们会使用决策语句。基本用法如下 -

If <condition> is true, Then <do this>, Else 

在一些不同的编程语言中,有 If-Else 语句,但在 Prolog 中我们必须以其他方式定义语句。以下是 Prolog 中决策的示例。

程序

% If-Then-Else statement

gt(X,Y) :- X >= Y,write('X is greater or equal').
gt(X,Y) :- X < Y,write('X is smaller').

% If-Elif-Else statement

gte(X,Y) :- X > Y,write('X is greater').
gte(X,Y) :- X =:= Y,write('X and Y are same').
gte(X,Y) :- X < Y,write('X is smaller').

输出

| ?- [test].
compiling D:/TP Prolog/Sample_Codes/test.pl for byte code...
D:/TP Prolog/Sample_Codes/test.pl compiled, 3 lines read - 529 bytes written, 15 ms

yes
| ?- gt(10,100).
X is smaller

yes
| ?- gt(150,100).
X is greater or equal

true ?

yes
| ?- gte(10,20).
X is smaller

(15 ms) yes
| ?- gte(100,20).
X is greater

true ?

yes
| ?- gte(100,100).
X and Y are same

true ?

yes
| ?-

Prolog - 连词与析取

在本章中,我们将讨论合取和析取性质。这些属性在其他编程语言中使用 AND 和 OR 逻辑。Prolog 在其语法中也使用相同的逻辑。

连词

可以使用逗号 (,) 运算符来实现连接(AND 逻辑)。因此,用逗号分隔的两个谓词用 AND 语句连接起来。假设我们有一个谓词parent(jhon, bob),这意味着“Jhon是Bob的父母”,另一个谓词male(jhon),这意味着“Jhon是男性”。所以我们可以做另一个谓词father(jhon,bob),这意味着“Jhon是Bob的父亲”。当他是父母并且他是男性时,我们可以定义谓词father

析取

析取(OR 逻辑)可以使用分号(;)运算符来实现。因此,用分号分隔的两个谓词用 OR 语句连接起来。假设我们有一个谓词,father(jhon, bob)。这表明“Jhon 是 Bob 的父亲”,另一个谓词mother(lili,bob)表明“lili 是 bob 的母亲”。如果我们创建另一个谓词child() ,则当father(jhon, bob)为真或 mother(lili,bob)为真时,这将为真。

程序

parent(jhon,bob).
parent(lili,bob).

male(jhon).
female(lili).

% Conjunction Logic
father(X,Y) :- parent(X,Y),male(X).
mother(X,Y) :- parent(X,Y),female(X).

% Disjunction Logic
child_of(X,Y) :- father(X,Y);mother(X,Y).

输出

| ?- [conj_disj].
compiling D:/TP Prolog/Sample_Codes/conj_disj.pl for byte code...
D:/TP Prolog/Sample_Codes/conj_disj.pl compiled, 11 lines read - 1513 bytes written, 24 ms

yes
| ?- father(jhon,bob).

yes
| ?- child_of(jhon,bob).

true ?

yes
| ?- child_of(lili,bob).

yes
| ?-

Prolog - 列表

在本章中,我们将讨论 Prolog 中的重要概念之一:列表。它是一种可在不SymPy况下用于非数字编程的数据结构。列表用于将Atomics存储为集合。

在后续部分中,我们将讨论以下主题 -

  • Prolog 中列表的表示

  • Prolog 的基本操作,例如插入、删除、更新、追加。

  • 重新定位运算符,例如排列、组合等。

  • 集合运算如集合并集、集合交集等。

列表的表示

列表是一种简单的数据结构,广泛应用于非数值编程中。列表由任意数量的项目组成,例如红色、绿色、蓝色、白色、黑色。它将表示为[红、绿、蓝、白、暗]。元素列表将用方括号括起来。

列表可以为或非。在第一种情况下,列表简单地写为 Prolog Atomics []。在第二种情况下,列表由以下两部分组成 -

  • 第一项,称为列表的头部;

  • 列表的剩余部分称为tail

假设我们有一个类似的列表:[红,绿,蓝,白,暗]。这里头部是红色的,尾巴是[绿色,蓝色,白色,深色]。所以尾部是另一个列表。

现在,让我们考虑有一个列表,L = [a, b, c]。如果我们写 Tail = [b, c] 那么我们也可以将列表 L 写为 L = [ a | 尾巴]。这里,竖线(|)将头部和尾部分开。

因此以下列表表示也是有效的 -

  • [a,b,c] = [x | [b、c]]

  • [a,b,c] = [a,b | [C] ]

  • [a,b,c] = [a,b,c | [ ] ]

对于这些属性,我们可以将列表定义为 -

一种数据结构,要么是空的,要么由两部分组成——头和尾。尾部本身必须是一个列表。

列表的基本操作

下表包含序言列表上的各种操作 -

运营 定义
会员查询 在此操作期间,我们可以验证给定元素是否是指定列表的成员?
长度计算 通过这个操作,我们可以求出列表的长度。
级联 连接是一种用于连接/添加两个列表的操作。
删除项目 此操作从列表中删除指定的元素。
追加项目 追加操作将一个列表添加到另一个列表中(作为一项)。
插入项目 此操作将给定项目插入到列表中。

会员运营

在这个操作中,我们可以检查成员X是否存在于列表L中?那么如何检查这一点呢?好吧,我们必须定义一个谓词才能做到这一点。假设谓词名称为list_member(X,L)。该谓词的目标是检查 X 是否存在于 L 中。

为了设计这个谓词,我们可以遵循这些观察结果。X 是 L 的成员,如果 -

  • X 是 L 的头部,或者

  • X是L尾部的成员

程序

list_member(X,[X|_]).
list_member(X,[_|TAIL]) :- list_member(X,TAIL).

输出

| ?- [list_basics].
compiling D:/TP Prolog/Sample_Codes/list_basics.pl for byte code...
D:/TP Prolog/Sample_Codes/list_basics.pl compiled, 1 lines read - 467 bytes written, 13 ms

yes
| ?- list_member(b,[a,b,c]).

true ?

yes
| ?- list_member(b,[a,[b,c]]).

no
| ?- list_member([b,c],[a,[b,c]]).

true ?

yes
| ?- list_member(d,[a,b,c]).

no
| ?- list_member(d,[a,b,c]).

长度计算

这用于查找列表 L 的长度。我们将定义一个谓词来完成此任务。假设谓词名称为list_length(L,N)。这将 L 和 N 作为输入参数。这将对列表 L 中的元素进行计数,并将 N 实例化为其数量。与我们之前涉及列表的关系的情况一样,考虑两种情况是有用的 -

  • 如果列表为空,则长度为 0。

  • 如果链表不为空,那么L = [Head|Tail],那么它的长度就是1+Tail的长度。

程序

list_length([],0).
list_length([_|TAIL],N) :- list_length(TAIL,N1), N is N1 + 1.

输出

| ?- [list_basics].
compiling D:/TP Prolog/Sample_Codes/list_basics.pl for byte code...
D:/TP Prolog/Sample_Codes/list_basics.pl compiled, 4 lines read - 985 bytes written, 23 ms

yes
| ?- list_length([a,b,c,d,e,f,g,h,i,j],Len).

Len = 10

yes
| ?- list_length([],Len).

Len = 0

yes
| ?- list_length([[a,b],[c,d],[e,f]],Len).

Len = 3

yes
| ?-

级联

两个列表的串联意味着将第二个列表的列表项添加到第一个列表之后。因此,如果两个列表是 [a,b,c] 和 [1,2],那么最终列表将是 [a,b,c,1,2]。因此,为了完成此任务,我们将创建一个名为 list_concat() 的谓词,它将采用第一个列表 L1、第二个列表 L2 和 L3 作为结果列表。这里有两个观察结果。

  • 如果第一个列表为空,第二个列表为 L,则结果列表将为 L。

  • 如果第一个列表不为空,则将其写为[Head|Tail],将Tail与L2递归连接,并以[Head|New List]的形式存储到新列表中。

程序

list_concat([],L,L).
list_concat([X1|L1],L2,[X1|L3]) :- list_concat(L1,L2,L3).

输出

| ?- [list_basics].
compiling D:/TP Prolog/Sample_Codes/list_basics.pl for byte code...
D:/TP Prolog/Sample_Codes/list_basics.pl compiled, 7 lines read - 1367 bytes written, 19 ms

yes
| ?- list_concat([1,2],[a,b,c],NewList).

NewList = [1,2,a,b,c]

yes
| ?- list_concat([],[a,b,c],NewList).

NewList = [a,b,c]

yes
| ?- list_concat([[1,2,3],[p,q,r]],[a,b,c],NewList).

NewList = [[1,2,3],[p,q,r],a,b,c]

yes
| ?-

从列表中删除

假设我们有一个列表 L 和一个元素 X,我们必须从 L 中删除 X。所以有三种情况 -

  • 如果X是唯一的元素,那么删除它后,它将返回空列表。

  • 如果X是L的头部,则结果列表将是尾部部分。

  • 如果 X 存在于 Tail 部分,则从那里递归删除。

程序

list_delete(X, [X], []).
list_delete(X,[X|L1], L1).
list_delete(X, [Y|L2], [Y|L1]) :- list_delete(X,L2,L1).

输出

| ?- [list_basics].
compiling D:/TP Prolog/Sample_Codes/list_basics.pl for byte code...
D:/TP Prolog/Sample_Codes/list_basics.pl compiled, 11 lines read - 1923 bytes written, 25 ms

yes
| ?- list_delete(a,[a,e,i,o,u],NewList).

NewList = [e,i,o,u] ?

yes
| ?- list_delete(a,[a],NewList).

NewList = [] ?

yes
| ?- list_delete(X,[a,e,i,o,u],[a,e,o,u]).

X = i ? ;

no
| ?-

追加到列表中

附加两个列表意味着将两个列表添加在一起,或将一个列表添加为一项。现在,如果该项目存在于列表中,则追加功能将不起作用。因此,我们将创建一个谓词,即 list_append(L1, L2, L3)。以下是一些观察结果 -

  • 设A是一个元素,L1是一个列表,当L1已经有A时,输出也将是L1。

  • 否则新列表将为 L2 = [A|L1]。

程序

list_member(X,[X|_]).
list_member(X,[_|TAIL]) :- list_member(X,TAIL).

list_append(A,T,T) :- list_member(A,T),!.
list_append(A,T,[A|T]).

在本例中,我们使用了 (!) 符号,即所谓的剪切。所以当第一行执行成功后,我们就把它剪掉,这样就不会执行接下来的操作了。

输出

| ?- [list_basics].
compiling D:/TP Prolog/Sample_Codes/list_basics.pl for byte code...
D:/TP Prolog/Sample_Codes/list_basics.pl compiled, 14 lines read - 2334 bytes written, 25 ms

(16 ms) yes
| ?- list_append(a,[e,i,o,u],NewList).

NewList = [a,e,i,o,u]

yes
| ?- list_append(e,[e,i,o,u],NewList).

NewList = [e,i,o,u]

yes
| ?- list_append([a,b],[e,i,o,u],NewList).

NewList = [[a,b],e,i,o,u]

yes
| ?-

插入列表

此方法用于将项目 X 插入列表 L 中,结果列表将是 R。因此谓词将采用这种形式 list_insert(X, L, R)。所以这可以将X插入L中所有可能的位置。如果我们看得更近,就会有一些观察结果。

  • 如果我们执行list_insert(X,L,R),我们可以使用list_delete(X,R,L),因此从R中删除X并创建新列表L。

程序

list_delete(X, [X], []).
list_delete(X,[X|L1], L1).
list_delete(X, [Y|L2], [Y|L1]) :- list_delete(X,L2,L1).

list_insert(X,L,R) :- list_delete(X,R,L).

输出

| ?- [list_basics].
compiling D:/TP Prolog/Sample_Codes/list_basics.pl for byte code...
D:/TP Prolog/Sample_Codes/list_basics.pl compiled, 16 lines read - 2558 bytes written, 22 ms

(16 ms) yes
| ?- list_insert(a,[e,i,o,u],NewList).

NewList = [a,e,i,o,u] ? a

NewList = [e,a,i,o,u]

NewList = [e,i,a,o,u]

NewList = [e,i,o,a,u]

NewList = [e,i,o,u,a]

NewList = [e,i,o,u,a]

(15 ms) no
| ?-

列表项的重新定位操作

以下是重新定位操作 -

重新定位操作 定义
排列 此操作将更改列表项位置并生成所有可能的结果。
反转项目 此操作以相反的顺序排列列表中的项目。
班次项目 此操作会将列表中的一个元素旋转地向左移动。
订购商品 此操作验证给定列表是否有序。

排列运算

此操作将更改列表项位置并生成所有可能的结果。所以我们将创建一个谓词list_perm(L1,L2),这将生成L1的所有排列,并将它们存储到L2中。为此,我们需要 list_delete() 子句来提供帮助。

为了设计这个谓词,我们可以遵循下面给出的一些观察结果 -

X 是 L 的成员,如果 -

  • 如果第一个列表为空,则第二个列表也必须为空。

  • 如果第一个列表不为空,则其形式为 [X | L],这样一个列表的排列可以构造为,首先对L进行排列得到L1,然后将X在任意位置插入到L1中。

程序

list_delete(X,[X|L1], L1).
list_delete(X, [Y|L2], [Y|L1]) :- list_delete(X,L2,L1).

list_perm([],[]).
list_perm(L,[X|P]) :- list_delete(X,L,L1),list_perm(L1,P).

输出

| ?- [list_repos].
compiling D:/TP Prolog/Sample_Codes/list_repos.pl for byte code...
D:/TP Prolog/Sample_Codes/list_repos.pl compiled, 4 lines read - 1060 bytes written, 17 ms

(15 ms) yes
| ?- list_perm([a,b,c,d],X).

X = [a,b,c,d] ? a

X = [a,b,d,c]

X = [a,c,b,d]

X = [a,c,d,b]

X = [a,d,b,c]

X = [a,d,c,b]

X = [b,a,c,d]

X = [b,a,d,c]

X = [b,c,a,d]

X = [b,c,d,a]

X = [b,d,a,c]

X = [b,d,c,a]

X = [c,a,b,d]

X = [c,a,d,b]

X = [c,b,a,d]

X = [c,b,d,a]

X = [c,d,a,b]

X = [c,d,b,a]

X = [d,a,b,c]

X = [d,a,c,b]

X = [d,b,a,c]

X = [d,b,c,a]

X = [d,c,a,b]

X = [d,c,b,a]

(31 ms) no
| ?-

反向操作

假设我们有一个列表 L = [a,b,c,d,e],并且我们想要反转元素,因此输出将是 [e,d,c,b,a]。为此,我们将创建一个子句 list_reverse(List, ReversedList)。以下是一些观察结果 -

  • 如果列表为空,则结果列表也将为空。

  • 否则放置列表项,即[Head|Tail],并递归地反转Tail项,并与Head连接。

  • 否则放置列表项,即[Head|Tail],并递归地反转Tail项,并与Head连接。

程序

list_concat([],L,L).
list_concat([X1|L1],L2,[X1|L3]) :- list_concat(L1,L2,L3).

list_rev([],[]).
list_rev([Head|Tail],Reversed) :-
   list_rev(Tail, RevTail),list_concat(RevTail, [Head],Reversed).

输出

| ?- [list_repos].
compiling D:/TP Prolog/Sample_Codes/list_repos.pl for byte code...
D:/TP Prolog/Sample_Codes/list_repos.pl compiled, 10 lines read - 1977 bytes written, 19 ms

yes
| ?- list_rev([a,b,c,d,e],NewList).

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

yes
| ?- list_rev([a,b,c,d,e],[e,d,c,b,a]).

yes
| ?-

移位操作

使用 Shift 操作,我们可以将列表中的一个元素旋转地向左移动。因此,如果列表项是[a,b,c,d],那么移位后,它将是[b,c,d,a]。所以我们将创建一个子句list_shift(L1, L2)。

  • 我们将列表表示为[Head|Tail],然后递归地将Head和Tail连接起来,这样我们就能感觉到元素发生了移位。

  • 这也可以用来检查两个列表是否移动到一个位置。

程序

list_concat([],L,L).
list_concat([X1|L1],L2,[X1|L3]) :- list_concat(L1,L2,L3).

list_shift([Head|Tail],Shifted) :- list_concat(Tail, [Head],Shifted).

输出

| ?- [list_repos].
compiling D:/TP Prolog/Sample_Codes/list_repos.pl for byte code...
D:/TP Prolog/Sample_Codes/list_repos.pl compiled, 12 lines read - 2287 bytes written, 10 ms

yes
| ?- list_shift([a,b,c,d,e],L2).

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

(16 ms) yes
| ?- list_shift([a,b,c,d,e],[b,c,d,e,a]).

yes
| ?-

订单操作

这里我们将定义一个谓词 list_order(L) 来检查 L 是否有序。因此,如果 L = [1,2,3,4,5,6],则结果将为 true。

  • 如果只有一个元素,则该元素已排序。

  • 否则,将前两个元素 X 和 Y 作为 Head,其余作为 Tail。如果 X =< Y,则使用参数 [Y|Tail] 再次调用该子句,因此将从下一个元素开始递归检查。

程序

list_order([X, Y | Tail]) :- X =< Y, list_order([Y|Tail]).
list_order([X]).

输出

| ?- [list_repos].
compiling D:/TP Prolog/Sample_Codes/list_repos.pl for byte code...
D:/TP Prolog/Sample_Codes/list_repos.pl:15: warning: singleton variables [X] for list_order/1
D:/TP Prolog/Sample_Codes/list_repos.pl compiled, 15 lines read - 2805 bytes written, 18 ms

yes
| ?- list_order([1,2,3,4,5,6,6,7,7,8]).

true ?

yes
| ?- list_order([1,4,2,3,6,5]).

no
| ?-

对列表进行设置操作

我们将尝试编写一个子句来获取给定集合的所有可能子集。因此,如果集合是 [a,b],那么结果将是 [], [a], [b], [a,b]。为此,我们将创建一个子句 list_subset(L, X)。它将采用 L 并将每个子集返回到 X 中。因此我们将按以下方式进行 -

  • 如果列表为空,则子集也为空。

  • 通过保留 Head 递归地找到子集,并且

  • 进行另一个递归调用,我们将删除 Head。

程序

list_subset([],[]).
list_subset([Head|Tail],[Head|Subset]) :- list_subset(Tail,Subset).
list_subset([Head|Tail],Subset) :- list_subset(Tail,Subset).

输出

| ?- [list_set].
compiling D:/TP Prolog/Sample_Codes/list_set.pl for byte code...
D:/TP Prolog/Sample_Codes/list_set.pl:3: warning: singleton variables [Head] for list_subset/2
D:/TP Prolog/Sample_Codes/list_set.pl compiled, 2 lines read - 653 bytes written, 7 ms

yes
| ?- list_subset([a,b],X).

X = [a,b] ? ;

X = [a] ? ;

X = [b] ? ;

X = []

(15 ms) yes
| ?- list_subset([x,y,z],X).

X = [x,y,z] ? a

X = [x,y]

X = [x,z]

X = [x]

X = [y,z]

X = [y]

X = [z]

X = []

yes
| ?-

联盟运作

让我们定义一个名为 lis 的子句