SWI-Prolog中事实声明顺序为何影响合一查询行为?
测试用事实定义
dnp_padrão(1, plural, mos). dnp_padrão(2, plural, 'is'). dnp_padrão(3, plural, m). dnp_padrão(1, singular, ''). dnp_padrão(2, singular, s). dnp_padrão(3, singular, '').
行为差异的核心原因
这个差异和Prolog的合一逻辑无关,是SWI-Prolog默认的子句索引优化导致的,两个查询的执行逻辑本质没有区别,只是引擎对「匹配完成后是否还有剩余待检测子句」的预判结果不同。
SWI-Prolog默认会为所有静态谓词的第一个参数建立哈希索引:执行查询时,如果第一个参数是已经绑定值的实例化状态,引擎会直接跳过所有第一个参数和查询值不相等的子句,只遍历同哈希桶下的候选子句,避免全量遍历所有子句。
对上述6条事实,第一个参数值为1的子句只有2条,按断言顺序排列在索引桶的链表里,顺序是:
- 第一条:
dnp_padrão(1, plural, mos) - 第四条:
dnp_padrão(1, singular, '')
两个查询的执行流程分别如下:
第一个查询
dnp_padrão(1, singular, X):
引擎进入1对应的索引桶,按顺序先尝试第一条子句,第二个参数是plural和查询的singular不匹配,跳过;继续尝试第四条子句,第二个参数匹配,合一成功,X绑定为''。此时第四条子句是这个索引桶链表的最后一个节点,引擎明确知道后面没有任何待检测的候选子句,所以直接返回结果,不会提示用户输入;检索下一个解。第二个查询
dnp_padrão(1, plural, X):
引擎进入1对应的索引桶,按顺序尝试第一条子句就匹配成功,X绑定为mos。但此时这条子句后面还有同桶的第四条子句待检测,默认索引只覆盖第一个参数,引擎不会提前预判第四条子句的第二个参数是否匹配,所以会暂停等待用户选择是否继续检索。如果用户输入;要求下一个解,引擎会回溯尝试第四条子句,第二个参数不匹配,遍历到桶尾无更多结果,最终返回false。
Prolog合一与子句检索的标准逻辑
索引只是性能优化手段,不会改变Prolog的核心执行规则,不管有没有索引,子句匹配和合一的标准流程是固定的:
- 所有子句严格按照断言书写的顺序从上到下依次尝试匹配
- 对每条待检测子句,逐位置做合一判断:变量可以和任意项绑定,原子、复合项要求结构和值完全一致才能匹配
- 某条子句匹配成功后,就返回当前的变量绑定结果;如果用户请求更多解,引擎会撤销当前子句做的所有变量绑定,回溯到下一条子句继续尝试
- 所有子句都尝试完毕仍无匹配,返回
false
你可以通过手动添加多参数索引验证上述结论:在SWI-Prolog中先执行声明:- index(dnp_padrão(1,1,0)).,告知引擎为谓词的第一个、第二个参数都建立索引,再执行dnp_padrão(1, plural, X)查询,就会发现匹配完X = mos后会直接结束,不会再出现false提示——此时引擎通过双参数索引直接定位到(1,plural)对应的桶里只有1条子句,匹配完就没有剩余候选了。
内容的提问来源于stack exchange,提问作者Guilherme Namen

