Prolog:调用谓词时考虑动态模块中的子句

Prolog: consider clauses from a dynamic module when calling a predicate

SWI Prolog manual中,我发现了下面的备注:

For example, assume an application that can reason about multiple worlds. It is attractive to store the data of a particular world in a module, so we extract information from a world simply by invoking goals in this world.

这实际上很好地描述了我要实现的目标。不过我运行陷入了一个问题。虽然我确实想为许多不同的世界建模,但我也想在所有这些世界中分享一些东西。所以我的想法是为每个世界中真实的事物建立一个 allworlds 模块,为每个我想推理的世界建立一个模块,后者从前者导入。所以我会在 REPL 中做这样的事情:

allworlds:asserta(grandparent(X, Z) :- (parent(X, Y), parent(Y, Z))).
allworlds:dynamic(parent/2).
add_import_module(greece, allworlds, start).
greece:asserta(parent(kronos, zeus)).
greece:asserta(parent(zeus, ares)).

现在我想查询 greece:grandparent(kronos, X) 并得到 X = ares,但我得到的只是 false。当 allworlds:grandparent 调用 parent 时,它并没有像我希望的那样调用 greece:parent,而是 allworlds:parent。我的研究似乎表明我需要使 grandparent 谓词 module-t运行sparent。但是调用 allworlds:module_transparent(grandparent/2). 并没有解决问题,而且它也被弃用了。这就是我被困的地方。我怎样才能让这个工作? meta_predicate/1 是解决方案的一部分吗?不幸的是,我无法理解其文档的正面或反面。

Prolog 模块没有为“多世界”设计模式提供好的解决方案。值得注意的是,使谓词成为元谓词(或模块透明或多文件)将是一个有问题的 hack。但是这种模式对于 Logtalk 来说是微不足道的,它是一种扩展 Prolog 的语言,可以将大多数 Prolog 系统用作后端编译器。针对您的问题的最小(但不是唯一)解决方案是:

:- object(allworlds).

    :- public(grandparent/2).
    grandparent(X, Z) :-
        ::parent(X, Y),
        ::parent(Y, Z).

    :- public(parent/2).

:- end_object.


:- object(greece,
    extends(allworlds)).

    parent(kronos, zeus).
    parent(zeus, ares).

:- end_object.

在这里,当公共谓词需要访问世界时,我们使用继承(各个世界继承公共知识)和消息到 self::/1 控制结构)特定谓词定义(self 是接收消息的 object/world - 在示例中为 grandparent/2)。

假设代码保存在 worlds.lgt 文件中并且您使用 SWI-Prolog 作为后端:

$ swilgt
...
?- {worlds}.
% [ /Users/pmoura/worlds.lgt loaded ]
% (0 warnings)
true.

?- greece::grandparent(kronos, X).
X = ares.

P.S。如果在 windows 上 运行,请在安装 Logtalk 后使用开始菜单中的“Logtalk - SWI-Prolog”快捷方式。

我最终通过显式传递模块并使用 : 运算符调用其中的谓词解决了这个问题。它让我想起了在 C 中做 OOP 的一些事情,在那里你做类似 obj->vtable->method(obj, params) 的事情(注意 obj 被提到两次,就像我下面代码中的 M 一样)。

类似于Logtalk的解决方案,当我想考虑它的子句时,我需要显式地调用导入的模块。例如,我在 allworlds 模块中添加了父亲也是 parent 的事实。

allworlds:assertz(grandparent(M, X, Z) :- (M:parent(M, X, Y), M:parent(M, Y, Z))).
allworlds:assertz(parent(M, X, Y) :- M:father(M, X, Y)).
add_import_module(greece, allworlds, start).
greece:assertz(parent(_, kronos, zeus)).
% need to call into allworlds explicitly
greece:assertz(parent(M, X, Y) :- allworlds:parent(M, X, Y)). 
greece:assertz(father(_, zeus, ares)).

做出这些断言后,我可以调用 greece:grandparent(greece, kronos, X). 并获得预期结果 X = ares