问题
I'm trying to write a predicate move/3
which handles several kinds of terms, each kind of which is defined in a separate file. I'm trying to use modules for this, because the files contain other predicates which should be namespaced appropriately.
So, I've created a module cat.prolog
with contents:
:- module(cat, [move/3]).
:- multifile(move/3).
move(cat(C), P, cat(C2)) :-
...
Similarly for dog.prolog
.
And main.prolog
with:
:- 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.)
Trying to run this in 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.
At this point I can use dog:move/3
and cat:move/3
but not move/3
. It works for the cat
case but not the dog
case.
I get the feeling there's a really obvious way to do this. I've tried combining modules and imports and multifile directives in many ways and still not found it yet...
回答1:
The multifile/1 syntax is simple, but the documentation lacks a simple example...
I created 3 modules files: pets.pl
, cat.pl
, dog.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)).
Note the relevant syntax Module:Pred :- ...
in the 'dependent' files
?- [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.
?-
回答2:
A second working solution, this time without using the Logtalk extension to Prolog, following up my comment about using the include/1
directive:
----- 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].
Like in the Logtalk solution, multifile predicates are not the answer.
It should be noted that, in most Prolog module systems, including SWI-Prolog, any module predicate can be called using explicit qualification as in the queries above. But using a file to hold the common bits have still two advantages: (1) it makes explicit in a single place the common bits and avoids source code duplication; (2) well behaved applications should only call exported predicates and there are tools that can detect violation of this principle. On the down side, an included file is only a first class entity of you consider files itself as such. Care must also be taking when loading the modules that include the common bits file as usage of e.g. consult/1
or use_module/1
will result in conflicts:
?- [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.
In practice, this solution may result in most or all module predicates to be called using explicit qualification.
回答3:
An alternative solution is to define a Logtalk protocol (i.e. an interface) for declaring predicates such as move/3
and then define any number of objects implementing this protocol. You can run Logtalk using most Prolog compilers, including SWI-Prolog. For example:
:- 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.
Assuming that each entity above is defined in its own file, e.g. pets.lgt
, cat.lgt
, and dog.lgt
you can then do:
$ swilgt
...
?- {pets, cat, dog}.
...
? - cat::move(A, B, C).
...
Logtalk supports both prototypes and classes/instances so you can define any kind of hierarchy/program structure that fits your application. It's also likely a more clean solution than subverting multifile predicates to have different predicate implementations for different entities.
来源:https://stackoverflow.com/questions/21692835/define-parts-of-a-predicate-across-several-modules