SWI-Prolog 中的跨模块 "interface" 调用

A cross-module "interface" call in SWI-Prolog

这可能特定于 SWI Prolog 模块系统。

假设我们有三个 Prolog 模块(在 SWI-Prolog 模块系统中):

谓词robin:robin/0(即模块robin中的谓词robin_hood/0)和谓词arthur:arthur/0调用谓词helper:helper/2(由模块[=22导出) =]).

Predicate helper:helper/2 然后应该调用一个 predicate toolshed/1,根据调用者模块,它是 不同的 。我希望 helper/2 调用与调用 helper/2.

的谓词关联的 toolshed/1 谓词

在 Java 中,可以将带有 toolshed() 方法的接口传递给 helper(),以便 helper() 可以调用该接口并以正确的实现结束.

我如何在 Prolog 中执行此操作?

示例代码:

robin.pl

:- module(robin,
          [
           robin/0
          ,toolshed/1
          ]).

:- use_module(library('helper.pl')).

robin :- helper(friar_tuck,help).

toolshed('a katana made by mastersmith Masamune').
toolshed('an ancient shovel with a sharpened blade').
toolshed('an Uzi 9mm with Norinco markings').

arthur.pl

:- module(arthur,
          [
           arthur/0
          ,toolshed/1
          ]).

:- use_module(library('helper.pl')).

arthur :- helper(merlin,help).

toolshed('a slightly musty grimoire').
toolshed('a jar of mandragore').
toolshed('a fresh toadstool').

helper.pl

:- module(helper,
          [
          helper/2
          ]).

helper(friar_tuck,help) :-
   format("I will help you rout the Sheriff of Nottingham's men!~n"),
   setof(X,toolshed(X),Tools),
   format("I found these tools: ~q~n",[Tools]),
   format("Have at them!~n").

helper(merlin,help) :-
   format("I will help you rout Mordred's army!~n"),
   setof(X,toolshed(X),Tools),
   format("I found these tools: ~q~n",[Tools]),
   format("Have at them!~n").

把他们编成导演testing:

testing
├── arthur.pl
├── helper.pl
└── robin.pl

启动swipl,设置库搜索路径并加载arthur.pl:

?- assertz(file_search_path(library,'/home/me/testing')).
true.

?- use_module(library('arthur.pl')).
true.

?- arthur.
I will help you rout Mordred's army!
I found these tools: ['a fresh toadstool','a jar of mandragore','a slightly musty grimoire']
Have at them!
true.

所以这行得通。 toolshed/1 由模块 arthur 导出,并且由模块 helper 可见(并且可调用 unqalified),即使 helper 不导入 arthur.pl(不太确定它是如何工作的,也许属于当前在堆栈上的谓词的所有模块的导出谓词是可见的并且是未限定的可访问的?)。

但我也无法加载 robin.pl:

?- use_module(library('robin.pl')).
ERROR: import/1: No permission to import robin:toolshed/1 into user (already imported from arthur)
true.

好的,这并不奇怪。但是我怎样才能得到我想要的结果呢?我想看这个:

?- use_module(library('robin.pl')).
true.

?- robin.
I will help you rout the Sheriff of Nottingham's men!
I found these tools: ['a katana made by mastersmith Masamune','an Uzi 9mm with Norinco markings','an ancient shovel with a sharpened blade']
Have at them!
true.

看来我偶然发现了一个解决方案:

新helper.pl

它修改了 helper/2: now helper/3,它接受调用者的模块名称作为第三个参数,并使用它来限定对 toolshed/1:[=29= 的调用]

(是否可以在不创建堆栈跟踪的情况下找出当前正在调用哪个模块?)

:- module(helper,
          [
          helper/3
          ]).

helper(friar_tuck,help,Module) :-
   format("I will help you rout the Sheriff of Nottingham's men!~n"),
   setof(X,Module:toolshed(X),Tools),
   format("I found these tools: ~q~n",[Tools]),
   format("Have at them!~n").

helper(merlin,help,Module) :-
   format("I will help you rout Mordred's army!~n"),
   setof(X,Module:toolshed(X),Tools),
   format("I found these tools: ~q~n",[Tools]),
   format("Have at them!~n").

新arthur.pl

导出toolshed/1 and passes the name of the arthurmodule in the call tohelper:helper/3`。 (是否可以找到当前模块的名称?这种硬编码是一个等待发生的意外)。

:- module(arthur,
          [
           arthur/0
          ]).

:- use_module(library('helper.pl')).

arthur :- helper(merlin,help,arthur).  % Give module name as third argument

toolshed('a slightly musty grimoire').
toolshed('a jar of mandragore').
toolshed('a fresh toadstool').

新robin.pl

:- module(robin,
          [
           robin/0
          ]).

:- use_module(library('helper.pl')).

robin :- helper(friar_tuck,help,robin). % Give module name as third argument

toolshed('a katana made by mastersmith Masamune').
toolshed('an ancient shovel with a sharpened blade').
toolshed('an Uzi 9mm with Norinco markings').

而且确实有效。模块 arthurrobin 可以同时加载,因为 toolshed/1 不再导出,但仍然可以从 helper:helper/3.

使用

开始swipl,然后:

?- assertz(file_search_path(library,'/home/me/testing')).
true.

现在我可以加载两个模块了,因为在未导出的模块上没有冲突 toolshed/1:

?- use_module(library('robin.pl')).
true.

?- use_module(library('arthur.pl')).
true.

并且以合格的方式调用 toolshed/1 效果很好:

?- robin.
I will help you rout the Sheriff of Nottingham's men!
I found these tools: ['a katana made by mastersmith Masamune','an Uzi 9mm with Norinco markings','an ancient shovel with a sharpened blade']
Have at them!
true.

?- arthur.
I will help you rout Mordred's army!
I found these tools: ['a fresh toadstool','a jar of mandragore','a slightly musty grimoire']
Have at them!
true.

这似乎是对的,虽然我仍然不知道为什么非导出的toolshed/1实际上可以被调用。

一个有趣的问题。遵循完全可移植的 Logtalk 解决方案(对可移植性进行了细微更改,并避免了对讨厌的 double_quotes 标志的依赖;format/2 是事实上的标准谓词,它在第一个参数中接受原子,但谓词的方式是否可用取决于 Prolog 系统,无需将这些细节弄乱代码):

:- protocol(toolshed).

    :- public(toolshed/1).

:- end_protocol.


:- category(helper).

    :- private(helper/2).

    helper(friar_tuck, help) :-
        write('I will help you rout the Sheriff of Nottingham\'s men!\n'),
        setof(X, ::toolshed(X), Tools),
        write('I found these tools: '), writeq(Tools), nl,
        write('Have at them!\n').

    helper(merlin, help) :-
        write('I will help you rout Mordred\'s army!\n'),
        setof(X, ::toolshed(X), Tools),
        write('I found these tools: '), writeq(Tools), nl,
        write('Have at them!\n').

:- end_category.


:- object(robin,
    implements(toolshed),
    imports(helper)).

    :- public(robin_hood/0).

    robin_hood :-
        ^^helper(friar_tuck, help).

    toolshed('a katana made by mastersmith Masamune').
    toolshed('an ancient shovel with a sharpened blade').
    toolshed('an Uzi 9mm with Norinco markings').

:- end_object.


:- object(arthur,
    implements(toolshed),
    imports(helper)).

    :- public(arthur/0).

    arthur :-
        ^^helper(merlin, help).

    toolshed('a slightly musty grimoire').
    toolshed('a jar of mandragore').
    toolshed('a fresh toadstool').

:- end_object.

然后(假设上面的代码保存在dh.lgt文件中):

$ swilgt
...

?- {dh}.
...
true.

?- robin::robin_hood.
I will help you rout the Sheriff of Nottingham's men!
I found these tools: ['a katana made by mastersmith Masamune','an Uzi 9mm with Norinco markings','an ancient shovel with a sharpened blade']
Have at them!~n
true.

?- arthur::arthur.
I will help you rout Mordred's army!
I found these tools: ['a fresh toadstool','a jar of mandragore','a slightly musty grimoire']
Have at them!~n
true.

您可以 运行 此解决方案不仅适用于 Logtalk,而且适用于其所有受支持的后端 Prolog 系统。

如果您不希望 toolshed/1 谓词成为 public,您可以更改对象打开指令,使谓词成为受保护的或私有的。例如:

:- object(robin,
    implements(private::toolshed),
    imports(helper)).

这会给你:

?- arthur::toolshed(T).
!     Permission error: access private_predicate toolshed/1
!       in goal: arthur::toolshed(A)

您也可以更改协议以使谓词在那里私有化,但这不是惯用的。

您也可以使您的代码与多文件谓词一起工作。 [更新:这解决了加载冲突,但没有更多更改robinarthur 可以相互使用工具。] 但是 主要的多文件谓词声明 (即没有显式模块限定的那个)必须在 helper模块:

:- module(helper, [helper/2]).

:- multifile(toolshed/1).

helper(friar_tuck,help) :-
   format("I will help you rout the Sheriff of Nottingham's men!~n"),
   setof(X,toolshed(X),Tools),
   format("I found these tools: ~q~n",[Tools]),
   format("Have at them!~n").

helper(merlin,help) :-
   format("I will help you rout Mordred's army!~n"),
   setof(X,toolshed(X),Tools),
   format("I found these tools: ~q~n",[Tools]),
   format("Have at them!~n").

arthur模块变为:

:- module(arthur, [arthur/0]).

:- use_module('helper.pl').

arthur :- helper(merlin,help).

:- multifile(helper:toolshed/1).
helper:toolshed('a slightly musty grimoire').
helper:toolshed('a jar of mandragore').
helper:toolshed('a fresh toadstool').

robin 模块类似:

:- module(robin, [robin/0]).

:- use_module('helper.pl').

robin :- helper(friar_tuck,help).

:- multifile(helper:toolshed/1).
helper:toolshed('a katana made by mastersmith Masamune').
helper:toolshed('an ancient shovel with a sharpened blade').
helper:toolshed('an Uzi 9mm with Norinco markings').

调用示例:

?- [helper, arthur, robin].
true.

?- arthur.
I will help you rout Mordred's army!
I found these tools: ['a fresh toadstool','a jar of mandragore','a katana made by mastersmith Masamune','a slightly musty grimoire','an Uzi 9mm with Norinco markings','an ancient shovel with a sharpened blade']
Have at them!
true.

?- robin.
I will help you rout the Sheriff of Nottingham's men!
I found these tools: ['a fresh toadstool','a jar of mandragore','a katana made by mastersmith Masamune','a slightly musty grimoire','an Uzi 9mm with Norinco markings','an ancient shovel with a sharpened blade']
Have at them!
true.

顺便说一句,永远不要写:

:- use_module(library('helper.pl')).

当您加载与导入模块位于同一目录中的另一个模块时。这只是在自找麻烦,因为 library 是一个目录别名,可能具有多个定义(按照您很可能无法控制的顺序)以及搜索机制。

使用 SWI-Prolog 专有 module-transparent mechanism 确实提供了一个可行的替代方案。但要注意,这种机制不仅被标记为“不推荐由程序员直接使用”。但也有该文档中未提及的其他问题。

在此解决方案中,我们使 helper/2 谓词 模块透明 :

:- module(helper, [helper/2]).

:- module_transparent(helper/2).

helper(friar_tuck,help) :-
   format("I will help you rout the Sheriff of Nottingham's men!~n"),
   setof(X,toolshed(X),Tools),
   format("I found these tools: ~q~n",[Tools]),
   format("Have at them!~n").

helper(merlin,help) :-
   format("I will help you rout Mordred's army!~n"),
   setof(X,toolshed(X),Tools),
   format("I found these tools: ~q~n",[Tools]),
   format("Have at them!~n").

其他模块则简化为:

:- module(arthur, [arthur/0]).

:- use_module('helper.pl').

arthur :- helper(merlin,help).

toolshed('a slightly musty grimoire').
toolshed('a jar of mandragore').
toolshed('a fresh toadstool').

和:

:- module(robin, [robin/0]).

:- use_module('helper.pl').

robin :- helper(friar_tuck,help).

toolshed('a katana made by mastersmith Masamune').
toolshed('an ancient shovel with a sharpened blade').
toolshed('an Uzi 9mm with Norinco markings').

然后我们得到:

?- [helper, arthur, robin].
true.

?- arthur.
I will help you rout Mordred's army!
I found these tools: ['a fresh toadstool','a jar of mandragore','a slightly musty grimoire']
Have at them!
true.

?- robin.
I will help you rout the Sheriff of Nottingham's men!
I found these tools: ['a katana made by mastersmith Masamune','an Uzi 9mm with Norinco markings','an ancient shovel with a sharpened blade']
Have at them!
true.

也就是说,这个和其他模块解决方案有几个问题,并且不能很好地扩展到更复杂的情况,因为您正在寻找的功能(值得注意的是,interfaces/protocols 作为第一个 class 构造)在 Prolog 模块系统中不存在,并且 hack 不足(参见 https://logtalk.org/blog.html?tag=half+broken+hacks)。