4.5 联合
本节描述用逻辑语言执行推理的查询解释器的实现。 解释器是一个一般的问题解决者,但是在它能解决的问题的规模和类型上有很大的限制。 虽然存在更复杂的逻辑程序设计语言,但构建高效推理程序仍然是计算机科学中一个活跃的研究课题。
查询解释器执行的基本操作称为联合。联合是将查询与事实相匹配的一般方法,每个查询都可能包含变量。查询解释器反复应用此操作,首先将原始查询与事实结论进行匹配,然后将事实假设与数据库中的其他结论进行匹配。 在此过程中,查询解释器在与查询相关的所有事实空间中执行搜索。 如果它找到一种方法,通过向变量赋值来支持该查询,那么它将作为成功的结果返回该赋值。
4.5.1 模式匹配
为了返回与查询匹配的简单事实,解释器必须将包含变量的查询与不包含变量的事实匹配。 例如,如果变量?child的值为barack,则查询(query(父abraham ?child))和事实(fact(父abraham barack))匹配。
通常,如果存在变量名与值的绑定,将这些值替换到模式中就会生成表达式,那么模式就会匹配某个表达式(可能是嵌套的模式列表)。
例如,表达式((a b) c (a b))匹配模式(? 同样的表达式匹配模式((a ?y) ?z (a b)),变量?y绑定b,?z绑定c。
4.5.2 表达事实和查询
通过导入提供的逻辑示例程序,可以复制以下示例。
查询和事实都在逻辑语言中表示为Scheme列表,使用前一章中的相同的Pair类和nil对象。例如,查询表达式(?x c ?x)表示为嵌套的Pair实例。
与在Scheme项目中一样,将符号绑定到值的环境用Frame类的实例表示,该实例有一个名为bindings的属性。
在逻辑语言中执行模式匹配的函数称为unify。它接受两个输入,e和f,以及一个记录变量到值的绑定的环境env。
上面,unify的返回值为True表明模式f能够匹配表达式e。unify 的结果记录在env中的?x到(a b)的绑定中。
4.5.3 联合算法
联合是模式匹配的概括,它试图找到两个可能都包含变量的表达式之间的映射。unify 函数通过递归过程实现联合,即对两个表达式的相应部分进行联合,直到达到矛盾或建立对所有变量的可行绑定。
让我们从一个例子开始。 模式(? x ?x)可以匹配模式((a ?y c) (a ?z)),因为有一个没有变量的表达式同时匹配:((a ? b c) (a ? b c))。 unify通过以下步骤确定这个解决方案:
为了匹配每个模式的第一个元素,变量?x被绑定到表达式(a ?y c)。
为了匹配每个模式的第二个元素,首先将变量?x替换为它的值。然后,通过将?y绑定到b,将?z绑定到c,将(a ?y c)匹配到(a ?y z)。
因此,放置在传递给unify的环境中的绑定包含了?x、?y和?z的条目:
联合的结果可能会将一个变量绑定到一个也包含变量的表达式上,正如我们在上面看到的?x bound to (a ?y c). bind函数递归和重复地将所有变量绑定到表达式中的值上,直到绑定的变量没有剩下为止。
一般来说,联合是要检查几个条件的。unify的实现直接遵循下面的描述。
如果输入e和f是变量,则用它们的值来替换。
如果e和f相等,则联合成功。
如果e是一个变量,联合成功,e绑定到f。
如果f是一个变量,联合成功,f绑定到e。
如果都不是一个变量,这两个不列出,他们是不平等的,然后e和f不能联合,所以联合失败。
如果这些情况都不成立,那么e和f都是一对,因此它们的第一个和第二个对应元素都是联合的。
4.5.4 证明
考虑逻辑语言的一种方式是将其视为一个正式系统中断言的证明。 每个陈述的事实在一个正式系统中建立一个公理,每个查询必须由查询解释器根据这些公理建立。 也就是说,每个查询都断言对其变量进行了某种赋值,以便它的所有子表达式同时遵循系统的事实。 查询解释器的角色是验证这一点。
例如,考虑到关于狗的一系列事实,我们可以断言克林顿和一只棕褐色的狗有共同的祖先。 查询解释器只输出Success! 如果它能证明这个论断是正确的。 作为一种副产品,它告诉我们共同祖先和棕褐色狗的名字:
结果中显示的三个作业中的每一个都是一个更大的证据,证明在给定事实的情况下查询是正确的。完整的证据应该包括所有使用的事实,例如包括(父母亚伯拉罕·克林顿)和(父母菲尔莫尔·亚伯拉罕)。
4.5.5 搜索
为了从系统中已经建立的事实建立查询,查询解释器在所有可能的事实空间中执行搜索。 联合是模式匹配两个表达式的基本操作。查询解释器中的搜索过程选择要联合的表达式,以便找到一组连接在一起以建立查询的事实。
递归搜索函数实现了逻辑语言的搜索过程。 它将查询中的子句的Scheme列表、包含符号到值的当前绑定(最初为空)的环境env和已经连接在一起的规则链的深度作为输入。
同时满足所有子句的搜索从第一个子句开始。 在第一个子句被否定的特殊情况下,我们不是试图将查询的第一个子句与一个事实联合起来,而是通过递归调用search来检查不可能存在这样的联合。 如果这个递归调用没有产生任何结果,则使用剩下的子句继续搜索过程。 如果联合是可能的,我们马上就失败了。
如果我们的第一个子句没有被否定,那么对于数据库中的每个事实,搜索尝试将事实的第一个子句与查询的第一个子句联合起来。 联合在一个新的环境env_head中执行。 作为联合的副作用,变量被绑定到env_head中的值。
如果联合成功,则该子句与当前规则的结论相匹配。 下面的for语句试图建立该规则的假设,从而得出结论。在这里,递归规则的假设将被递归地传递给搜索以建立。
最后,对于每一次成功的事实搜索。 第二,生成的环境被绑定到env_rule。 给定这些值到变量的绑定,最后的for语句在初始查询中搜索以建立其余的子句。 任何成功的结果都通过内部yield语句返回。
独特的名字。联合的前提是e和f之间没有共享变量。然而,我们经常在逻辑语言的事实和查询中重用变量名。 我们不想把?x和?x混淆;这些变量不相关。为了确保名称不会混淆,在将事实传递到unify之前,它的变量名将使用rename_variables替换为唯一的名称,并为事实附加一个唯一的整数。
其余的细节,包括逻辑语言的用户界面和各种helper函数的定义,出现在逻辑示例中。
Last updated
Was this helpful?