跨多个模块定义谓词的各个部分

时间:2014-02-11 04:00:15

标签: module prolog swi-prolog

我正在尝试编写一个谓词move/3来处理几种术语,每种术语都在一个单独的文件中定义。我正在尝试使用模块,因为这些文件包含其他应该适当命名的谓词。

所以,我创建了一个包含内容的模块cat.prolog

:- module(cat, [move/3]).
:- multifile(move/3).

move(cat(C), P, cat(C2)) :-
   ...

同样适用于dog.prolog

main.prolog与:

:- use_module(['cat.prolog'], [move/3]).
:- use_module(['dog.prolog'], [move/3]).

(various predicates that use move/3 and expecting the clauses from all imported modules to be applicable.)

尝试在SWI-Prolog中运行它:

?- ['main.prolog'].
%  cat.prolog compiled into cat 0.00 sec, 4,800 bytes
ERROR: Cannot import dog:move/3 into module user: already imported from cat
Warning: /home/edmund/main.prolog:2:
        Goal (directive) failed: user:use_module([dog.prolog],[move/3])
% main.prolog compiled 0.00 sec, 10,176 bytes
true.

此时我可以使用dog:move/3cat:move/3,但不能使用move/3。它适用于cat案例,但不适用于dog案例。

我觉得有一种非常明显的方法可以做到这一点。我尝试过以多种方式组合模块和导入以及多文件指令,但仍未找到它......

3 个答案:

答案 0 :(得分:5)

multifile / 1语法很简单,但documentation缺少一个简单的例子......

我创建了3个模块文件:pets.plcat.pldog.pl

:- module(pets, [test/0, move/3]).
:- multifile move/3.
move(A,B,C) :- writeln(pets-move(A,B,C)).
test :- forall(move(A,B,C), writeln(move(A,B,C))).

:- module(cat, []).
:- use_module(pets).
pets:move(A,B,C) :- writeln(cat-move(A,B,C)).

:- module(dog, []).
:- use_module(pets).
pets:move(A,B,C) :- writeln(dog-move(A,B,C)).

请注意“从属”文件中的相关语法Module:Pred :- ...

?- [cat,dog].
%  pets compiled into pets 0.00 sec, 3 clauses
% cat compiled into cat 0.01 sec, 7 clauses
% dog compiled into dog 0.00 sec, 3 clauses
true.

?- test.
Correct to: "pets:test"? yes
pets-move(_G41,_G42,_G43)
move(_G41,_G42,_G43)
cat-move(_G41,_G42,_G43)
move(_G41,_G42,_G43)
dog-move(_G41,_G42,_G43)
move(_G41,_G42,_G43)
true.

?- 

答案 1 :(得分:4)

第二个工作解决方案,这次没有使用Prod的Logtalk扩展,跟进我关于使用include/1指令的评论:

----- common.pl -----
:- export(move/1).
---------------------

----- cat.pl -----
:- module(cat, []).
:- include(common).

move(cat).
---------------------

----- dog.pl -----
:- module(dog, []).
:- include(common).

move(dog).
---------------------

$ swipl
...
?- use_module(cat, []), use_module(dog, []).
% cat compiled into cat 0.00 sec, 4 clauses
% dog compiled into dog 0.00 sec, 4 clauses
true.

?- cat:move(X).
X = cat.

?- dog:move(X).
X = dog.

?- module_property(cat, exports(Exports)).
Exports = [move/1].

?- module_property(dog, exports(Exports)).
Exports = [move/1].

与Logtalk解决方案类似,多文件谓词不是答案。

应该注意的是,在大多数Prolog模块系统中,包括SWI-Prolog,可以使用显式限定来调用任何模块谓词,如上面的查询中所述。但是使用文件来保存公共位还有两个优点:(1)它在一个地方明确公共位并避免源代码重复; (2)表现良好的应用程序应该只调用导出的谓词,并且有一些工具可以检测到违反这一原则。在不利方面,包含文件只是您考虑文件本身的第一类实体。在加载包含公共位文件的模块时也必须小心,例如使用公共位文件。 consult/1use_module/1会导致冲突:

?- [cat, dog].
% cat compiled into cat 0.00 sec, 4 clauses
ERROR: import/1: No permission to import dog:move/1 into user (already imported from cat)
% dog compiled into dog 0.00 sec, 4 clauses
true.

实际上,此解决方案可能会导致使用显式限定来调用大多数或所有模块谓词。

答案 2 :(得分:2)

另一种解决方案是定义Logtalk协议(即接口),用于声明诸如move/3之类的谓词,然后定义实现此协议的任意数量的对象。您可以使用大多数Prolog编译器运行Logtalk,包括SWI-Prolog。例如:

:- protocol(pets).

    :- public(move/3).
    ...

:- end_protocol.


:- object(cat, implements(pets)).

    move(A, B, C) :-
        ...

:- end_object.


:- object(dog, implements(pets)).

    move(A, B, C) :-
        ...

:- end_object.

假设上面的每个实体都在自己的文件中定义,例如然后您可以执行petscat.lgtdog.lgt

$ swilgt
...
?- {pets, cat, dog}.
...
? - cat::move(A, B, C).
...

Logtalk支持原型和类/实例,因此您可以定义适合您的应用程序的任何类型的层次结构/程序结构。它也可能是一个比颠覆多文件谓词更为干净的解决方案,以便为不同的实体提供不同的谓词实现。