【问题标题】:Prolog: consider clauses from a dynamic module when calling a predicateProlog:在调用谓词时考虑来自动态模块的子句
【发布时间】:2021-06-02 17:47:36
【问题描述】:

SWI Prolog manual中,我发现了如下注释:

例如,假设一个应用程序可以推理多个世界。将特定世界的数据存储在模块中很有吸引力,因此我们只需通过调用该世界中的目标来从世界中提取信息。

这实际上很好地描述了我想要实现的目标。但是我遇到了一个问题。虽然我确实想为许多不同的世界建模,但我也想在所有这些世界中分享一些东西。所以我的想法是有一个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 谓词模块透明。但是调用allworlds:module_transparent(grandparent/2). 并不能解决问题,而且它也已被弃用。这就是我卡住的地方。我怎样才能得到这个工作? meta_predicate/1 是解决方案的一部分吗?不幸的是,我无法对其文档做出正面或反面。

【问题讨论】:

标签: module prolog swi-prolog


【解决方案1】:

Prolog 模块不能为“多世界”设计模式提供良好的解决方案。值得注意的是,使谓词元谓词(或模块透明或多文件)将是一个有问题的黑客攻击。但是这种模式对于 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 是接收到消息的对象/世界 - 在示例中为 grandparent/2)。

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

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

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

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

【讨论】:

  • 嗨,保罗,谢谢你的回答。不幸的是,我的程序不是用 Prolog 编写的,它实际上是在 JVM 上运行的。它从外部获取对世界的描述,将其转换为 Prolog 事实,断言使用 JPL 的事实,进行一些查询,然后再次收回它们。我真的只是将它用作逻辑引擎。这仍然适用于 Logtalk 吗?不幸的是,上面的方法不能很好地与并发配合,这就是为什么我首先研究模块来分离不同的世界。
  • 对于使用 Logtalk + SWI-Prolog + JPL,参见例如logtalk.org/using_jpl.html
  • 嘿 Paolo,我终于开始尝试提出的解决方案,但我有一个问题:是否可以在已定义的派生对象中为谓词添加附加子句基础对象?例如,当我将grandparent(kronos, hephaistos). 添加到上面的greece 对象时,greece::grandparent(kronos, ares) 不再成立。没有明确的超级调用是否可以解决这个问题?
  • 您需要在派生对象中调用 super 以允许在本地定义之外找到继承的定义。参见例如logtalk.org/manuals/userman/…
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多