转载自:https://mp.weixin.qq.com/s/SeMjK-_XR69jSQEQ-iRHMg
本章有两个主要目标:
1. 讨论Prolog中的合一,并解释Prolog合一与标准合一有何不同。在此过程中,我们将introduce/ 2,Prolog合一的内置谓词,并与check / 2合一,即标准合一的内置谓词。
2. 解释Prolog在尝试使用惯用方式从旧信息中推断出新信息时使用的搜索策略。
1 合一
在上一章中使用知识库KB4时,我们简要提到了合一的思想。例如,我们说过Prolog将woman(X)与woman(mia)进行合一,从而将变量X实例化为mia。现在是时候仔细研究合一了,因为它是Prolog中最基本的思想之一。
回想一下,有三种项:
1.常数。这些可以是原子(如vincent)或数字(如24)。
2.变量。 (如X,Z3和List。)
3.复合项。它们具有以下形式:
functor(term_1,…,term_n).
我们将努力确定Prolog何时将两个项合一的绑定。我们的出发点将是以下绑定过程。它给出了基本的直觉,但在细节上略有改动:
两个项是合一的,还是它们包含可以用项合一实例化的变量,使得结果的项相等。
例如,这意味着项mia和mia合一,因为它们是同一原子。类似地,项42和42合一(因为它们是相同的数字),项X和X合一(因为它们是相同的变量),而项woman(mia)和woman(mia)合一,因为它们是相同的复合项。但是,项woman(mia)和woman(vincent)并不合一,因为它们不相同(并且它们都不包含可以实例化以使其相同的变量)。
现在,项mia和X呢?它们并不相同,但是变量X可以实例化为mia,这使它们相等。因此,根据我们的工作定义的第二部分,mia和X是合一的。类似地,项woman(X)和woman(mia)是合一的,因为可以通过将X实例化为mia来使它们相等。loves(vincent,X)和loves(X,mia)怎么样?不能。不可能找到使两个项相等的X的实例。你明白为什么吗?将X实例化为vincent会使我们得到loves(vincent,vincent)和loves(vincent,mia)这两个项,这显然是不相等的。但是,将X实例化为mia会产生loves(vincent,mia)和loves(mia,mia)项,两者也不相等。
通常,我们不仅对两个项合一这一事实感兴趣,而且我们还想知道如何实例化变量才能使其相等。而Prolog向我们提供了这些信息。当Prolog合一两个项时,它将执行所有必要的实例化,因此之后这些项实际上是相等的。此功能以及允许我们构建复杂项(即递归结构的项)的事实,使合一成为一种强大的编程机制。
基本直觉现在应该清楚了。这是使它们精确的定义。它不仅告诉我们Prolog将合一哪些项,而且还告诉我们变量将如何实现这一目标。
1.如果term1和term2是常数,则term1和term2当且仅当它们是相同的原子或相同的数字时才合一。
2.如果term1是变量,而term2是任何类型的项,则term1和term2合一,并且term1实例化为term2。同样,如果term2是变量,而term1是任何类型的term,则term1和term2合一,并且term2实例化为term1。 (因此,如果它们都是变量,则它们都将彼此实例化,并且我们说它们共享值。)
3.如果term1和term2是复合项,则只有在以下情况下它们才可以合一:
(a)它们具有相同的函子和元数,并且
(b)所有其相应的参数合一,并且
(c)变量实例是兼容的。 (例如,在合一一对参数时无法将变量X实例化为mia,而在合一另一对参数时则无法将变量X实例化为vincent。)
4.当且仅当这两个项是根据前三个条款的合一来合一的。
让我们看看这个定义的形式。第一个条款告诉我们两个常量何时合一。第二个条款告诉我们何时两个项(其中一个是变量)合一(这些项将始终合一;变量与任何事物合一)。同样重要的是,该条款还告诉我们必须执行哪些实例化才能使两个项相同。最后,第三个条款告诉我们两个复合项何时合一。注意此定义的结构。它的前三个条款完美地反映了项的(递归)结构。
第四条很重要:它说前三个条款告诉我们所有关于两个项合一的知识。如果使用1-3条无法显示两个项可以合一,那么它们就不能合一。例如,batman不与daughter(ink)合一。那么为什么呢?第一个项是一个常数,第二个项是一个复合项。但是前三条中没有一个告诉我们如何合一两个这样的项,因此(按第4条)它们不是合一的。
例子
为了确保我们完全理解此定义,让我们来看几个示例。在这些示例中,我们将使用一个重要的内置谓词= / 2谓词(回想一下,在末尾写/ 2表示该谓词带有两个参数)。
= / 2谓词测试其两个参数是否合一。例如,如果我们构成查询
?- =(mia,mia).
Prolog将回答true,如果我们提出查询
?- =(mia,vincent).
Prolog会回应 false.
但是我们通常不会以这种方式提出这些查询。面对现实吧,=(mia,mia)表示法是很不自然的。如果我们可以使用中缀表示法(即,可以将= / 2函子放在其参数之间)并编写类似以下内容,那就更好了:
?-mia = mia.
实际上,Prolog允许我们执行此操作,因此在以下示例中,我们将使用中缀表示法。
让我们回到第一个示例:
?- =(mia,mia).
true.
为什么Prolog会说true.?这似乎是一个愚蠢的问题:毫无疑问,x项的合一!是的,但是从上面给出的定义来看,这是怎么做的?重要的是要学会系统地思考合一(这对Prolog来说是根本的),而系统地思考意味着将示例与上述给出的合一定义联系起来。因此,让我们仔细考虑一下这个示例。
该定义包含三个条款。现在,条款2是针对一个参数是变量的情况,条款3是针对两个参数都是复合项的情况,因此在这里没有用。但是,条款1与我们的示例有关。这告诉我们两个常数在且仅当它们是完全相同的对象时才合一。由于mia和mia是同一原子,因此合一成功。
类似的参数解释了以下响应:
?- 2=2.
true.
?- mia = vincent.
false.
再次,条款1在这里是相关的(毕竟,2,mia和vincent都是常量)。由于2与2相同,并且mia与vincent是不同的原子,因此Prolog对第一个查询回答“true”,对第二个查询回答“false”。
但是,条款1确实给我们带来了一个小惊喜。考虑以下查询:
?- 'mia' = mia.
true.
这里发生了什么?为什么这两个项合一?好吧,就Prolog而言,“ mia”和mia是同一个原子。实际上,对于Prolog,任何形式为“符号”的原子都被视为与形式符号的原子相同的实体。在某些程序中,这可能是有用的功能,所以请不要忘记它。
另一方面,要查询
?- '2'=2.
false.
Prolog将回答false。而且,如果您考虑第1章中给出的定义,您将发现这必须是这样的工作方式。毕竟2是一个数字,而 ‘2’是一个原子。它们根本是不能相同的。
让我们尝试一个带有变量的示例:
?- mia = X.
X = mia.
再次,这是一个简单的示例:显然,变量X可以与常量mia合一,而Prolog可以这样做,并告诉我们它已经实现了合一。很好,但是从我们的定