SWI Prolog中如何让共享模块谓词调用动态模块的子句
问题根因
默认情况下,SWI Prolog的谓词内调用的目标是词法绑定的:即在哪个模块定义的谓词,内部的目标默认就在该模块内查找,所以allworlds模块下定义的grandparent规则里的parent,只会查找allworlds模块下的parent事实,不会自动切换到调用grandparent的greece模块。
解决方案
你可以用两种方式实现需求,meta_predicate/1确实是官方推荐的替代已废弃module_transparent/1的方案,也可以用更直观的显式上下文获取方案:
方案1:显式获取调用模块(无需理解元谓词逻辑)
直接修改grandparent的规则定义,通过context_module/1拿到调用该规则的模块,显式指定在该模块下查询parent:
% 替换原来的grandparent断言 allworlds:asserta(( grandparent(X, Z) :- context_module(CallerMod), CallerMod:parent(X, Y), CallerMod:parent(Y, Z) )).
修改后你原来的查询greece:grandparent(kronos, X)就会正确返回X = ares。
方案2:使用meta_predicate/1声明模块透明
在定义grandparent前,先在allworlds模块声明该谓词为模块透明,让其内部的所有目标调用都继承调用者的模块上下文:
% 先声明元谓词,替代已废弃的module_transparent allworlds:meta_predicate(grandparent(?, ?)). % 再断言规则,无需修改规则内部的parent调用 allworlds:asserta(grandparent(X, Z) :- (parent(X, Y), parent(Y, Z))).
额外优化建议
你原来在allworlds里声明的allworlds:dynamic(parent/2)是多余的,甚至会导致优先级问题,可以删除,改为在每个世界模块创建时,单独声明该模块下的parent/2为动态谓词即可:
% 示例,给greece模块声明parent/2动态 greece:dynamic(parent/2).
内容的提问来源于stack exchange,提问作者Matthias Berndt
相关产品推荐
相关产品推荐

