SWI-Prolog 中的跨模块“接口”调用

问题描述

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

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

  • def readBook(request,book_id): book = get_object_or_404(Book,pk=book_id) chapter_list = Chapter.objects.filter(book=book).order_by('-id') # you scpecify wich book the chapters are from context = {'book':book,'chapter_list':chapter_list} return render(request,'sophia/readbook.html',context) (在文件 robin 中)
  • robin.pl(在文件 arthur 中)
  • arthur.pl(在文件 helper 中)。

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

谓词 helper 然后应该调用谓词 helper:helper/2,它不同取决于调用者模块。我希望 toolshed/1 调用调用 helper/2 的谓词关联的 toolshed/1 谓词。

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

如何在 Prolog 中执行此操作?

calls

示例代码

robin.pl

helper()

arthur.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').

helper.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').

将它们放入导演 :- 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"),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 不导入 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.

解决方法

使用 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"),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.

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

,

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

新的 helper.pl

它有一个修改过的 helper/2: now helper/3,它接受​​调用者的模块名称作为第三个参数,并使用它来限定对 toolshed/1 的调用:

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

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

helper(friar_tuck,help,Module) :-
   format("I will help you rout the Sheriff of Nottingham's men!~n"),Module:toolshed(X),Module) :-
   format("I will help you rout Mordred's army!~n"),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,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,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 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 slightly musty grimoire']
Have at them!
true.

这似乎足够了,尽管我仍然不知道为什么实际上可以调用非导出的 toolshed/1

,

您也可以使您的代码使用多文件谓词。 [更新:这解决了加载冲突,但没有对 robin 和 {{ 1}} 可以相互使用工具。] 但是主要的多文件谓词声明(即没有显式模块限定的声明)必须在 arthur 模块上:

helper

:- module(helper,[helper/2]). :- multifile(toolshed/1). helper(friar_tuck,format("Have at them!~n"). 模块变为:

arthur

类似于 :- module(arthur,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,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,'a katana made by mastersmith Masamune','a slightly musty grimoire','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','an ancient shovel with a sharpened blade']
Have at them!
true.

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

,

一个有趣的问题。遵循完全可移植的 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'),::toolshed(X),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'),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,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 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 slightly musty grimoire']
Have at them!~n
true.

您不仅可以使用 Logtalk,还可以使用其所有支持的后端 Prolog 系统运行此解决方案。

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

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

这会给你:

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

您也可以更改协议以将谓词设为私有,但这并不符合习惯。