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
。
在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
。