我真的很愚蠢,觉得我想念一些东西。
我基本上有两个文件:
module.pl
用于通用逻辑规则(旨在可重用)state.pl
一种用于当前方案在模块文件(module.pl
)中,我声明:
inside(Food,Eater,T) :-
isTime(T),
injestEvent(InjEvent),
justAfter(T,InjEvent),
actorOfEvent(InjEvent, Eater),
objectOfEvent(InjEvent, Food).
Q1)我不得不用单例变量声明所有其他谓词(在同一文件中),只是为了阻止module.pl
抱怨它们不存在:
isTime(_T).
justAfter(_Time,_Event).
actorOfEvent(_Event, _ActorOfEvent).
objectOfEvent(_Event,_ActorOfEvent).
对吗?
Q2)我不能在其他文件中使用justAfter/2
这样的谓词,而不必说:
user:justAfter / 2的本地定义将覆盖从模块的弱导入
我如何使用从模块导入的谓词,而不是重新定义它?
答案 0 :(得分:3)
添加“面向对象”的基本形式非常简单。 假设我们在模块逻辑中有一个子句:
:- module(logic, [inside/4]).
% apply the rule to a specified module (expected to be a state object)
inside(M,Food,Eater,T) :-
M:isTime(T),
M:injestEvent(InjEvent),
M:justAfter(T, InjEvent),
M:actorOfEvent(InjEvent, Eater),
M:objectOfEvent(InjEvent, Food).
我们有很多相关的状态对象:在文件state1.pl
中isTime(10).
injestEvent(eat).
justAfter(10, eat).
actorOfEvent(eat, mick).
objectOfEvent(eat, food).
并处于文件state2.pl
isTime(20).
injestEvent(sleep).
justAfter(20, sleep).
actorOfEvent(sleep, everyone).
objectOfEvent(sleep, dream).
然后进行可能的会话:
?- [logic].
true.
?- s1:consult(state1).
true.
?- s2:consult(state2).
true.
?- inside(s1,Food,Eater,T).
Food = food,
Eater = mick,
T = 10.
?- inside(s2,What,Who,T).
What = dream,
Who = everyone,
T = 20.
一个小的概括,值得尝试:
inside(M,Food,Eater,T) :-
resolve(M),
M:isTime(T),
...
resolve / 1可能在的位置
resolve(M) :- var(M) -> current_module(M), catch(M:isTime(_),_,fail) ; true.
此技巧可启用“浏览对象”:
?- inside(M,X,Y,Z).
M = s2,
X = dream,
Y = everyone,
Z = 20 ;
M = s1,
X = food,
Y = mick,
Z = 10 ;
false.
答案 1 :(得分:2)
Prolog模块旨在隐藏辅助谓词。它们没有提供允许将谓词声明与谓词定义分开的接口的概念。因此,如果您导出未定义的谓词,编译器会抱怨。根据您的描述,我假设您尝试了以下操作:
----- module.pl -----
:- module(module, [
inside/3, isTime/1, injestEvent/1, justAfter/2, actorOfEvent/2, objectOfEvent/2
]).
inside(Food,Eater,T) :-
isTime(T),
injestEvent(InjEvent),
justAfter(T,InjEvent),
actorOfEvent(InjEvent, Eater),
objectOfEvent(InjEvent, Food).
---------------------
结果为:
?- [module].
ERROR: Exported procedure module:justAfter/2 is not defined
ERROR: Exported procedure module:isTime/1 is not defined
ERROR: Exported procedure module:injestEvent/1 is not defined
ERROR: Exported procedure module:objectOfEvent/2 is not defined
ERROR: Exported procedure module:actorOfEvent/2 is not defined
true.
您试图通过添加本地定义来解决此错误。但这只会导致您描述的第二个问题。当您执行以下操作时:
?- use_module(module).
您导入由module
导出的谓词,包括所有要在state.pl
中定义的谓词。因此,编译器在加载state.pl
时警告您,该文件将覆盖那些谓词。例如。与:
----- state.pl -----
isTime(1).
injestEvent(injEvent).
justAfter(1, injEvent).
actorOfEvent(injEvent, eater).
objectOfEvent(injEvent, food).
--------------------
我们得到:
?- [state].
Warning: /Users/pmoura/Desktop/state.pl:1:
Local definition of user:isTime/1 overrides weak import from module
Warning: /Users/pmoura/Desktop/state.pl:2:
Local definition of user:injestEvent/1 overrides weak import from module
Warning: /Users/pmoura/Desktop/state.pl:3:
Local definition of user:justAfter/2 overrides weak import from module
Warning: /Users/pmoura/Desktop/state.pl:4:
Local definition of user:actorOfEvent/2 overrides weak import from module
Warning: /Users/pmoura/Desktop/state.pl:5:
Local definition of user:objectOfEvent/2 overrides weak import from module
true.
尽管这些是警告,而不是错误,但调用inside/3
谓词将不会给您您想要的东西:
?- inside(Food,Eater,T).
true.
绑定在哪里?!?让我们跟踪调用以突出显示原因:
?- trace.
true.
[trace] ?- inside(Food,Eater,T).
Call: (8) module:inside(_2508, _2510, _2512) ? creep
Call: (9) module:isTime(_2512) ? creep
Exit: (9) module:isTime(_2512) ? creep
Call: (9) module:injestEvent(_2804) ? creep
Exit: (9) module:injestEvent(_2804) ? creep
Call: (9) module:justAfter(_2512, _2806) ? creep
Exit: (9) module:justAfter(_2512, _2806) ? creep
Call: (9) module:actorOfEvent(_2804, _2510) ? creep
Exit: (9) module:actorOfEvent(_2804, _2510) ? creep
Call: (9) module:objectOfEvent(_2804, _2508) ? creep
Exit: (9) module:objectOfEvent(_2804, _2508) ? creep
Exit: (8) module:inside(_2508, _2510, _2512) ? creep
true.
该跟踪清楚表明在错误上下文中正在调用“状态”谓词。
一个干净的解决方案是使用Logtalk对象而不是Prolog模块。 Logtalk扩展了Prolog,并支持大多数系统,包括SWI-Prolog。它支持接口/协议作为一等实体(解决您提到的第一个问题),并在其使用上下文中支持继承和调用谓词(解决第二个问题)。您可以使用例如
----- common.lgt -----
:- object(common).
:- public([
inside/3, isTime/1, injestEvent/1, justAfter/2, actorOfEvent/2, objectOfEvent/2
]).
inside(Food,Eater,T) :-
% call the next predicates in "self", i.e. in the
% object that received the inside/3 message
::isTime(T),
::injestEvent(InjEvent),
::justAfter(T,InjEvent),
::actorOfEvent(InjEvent, Eater),
::objectOfEvent(InjEvent, Food).
:- end_object.
----------------------
,然后将“状态”表示为:
----- state.lgt -----
:- object(state, extends(common)).
isTime(1).
injestEvent(injEvent).
justAfter(1, injEvent).
actorOfEvent(injEvent, eater).
objectOfEvent(injEvent, food).
:- end_object.
---------------------
快速测试(安装Logtalk之后):
$ swilgt
...
?- {common, state}.
...
true.
?- state::inside(Food,Eater,T).
Food = food,
Eater = eater,
T = 1.
作为奖励,您可以根据需要定义任意多个“状态”对象。您还可以在common
对象中为“状态”谓词提供默认定义。当“状态”对象未提供特定谓词的定义时,将继承并使用这些属性。例如,让我们在common
中添加以下子句:
objectOfEvent(injEvent, drink).
,然后从objectOfEvent(injEvent, food).
中删除(或注释掉)子句state
。保存并重新加载并重试查询将为您提供
?- {*}. % abbreviation for Logtalk's make
% Redefining object common
...
% Redefining object state
...
true.
?- state::inside(Food,Eater,T).
Food = drink,
Eater = eater,
T = 1.
如果需要,您还可以动态创建新的状态对象,而不用在源文件中定义它们。例如:
?- create_object(s2, [extends(common)], [], [isTime(42), ...]).
此可能不是您正在寻找的答案,但是在这种情况下,最佳答案是为作业使用正确的工具^ H ^ H ^ H ^ H封装机制。您的编程模式也是一种很常见的模式(这也是开发Logtalk的原因之一)。
答案 2 :(得分:2)
CapelliC的替代方法是使用Prolog dicts。它们已由SWI-Prolog引入,并且自1.3.0版以来,也可以在Jekejeke Prolog中获得。如果不需要接收器,则只需使用下划线即可。
文件状态1.pl:
:- module(state1, [isTime/2, injestEvent/2, justAfter/3,
actorOfEvent/3, objectOfEvent/3]).
:- reexport(logic).
_.isTime() := 10.
_.injestEvent() := eat.
_.justAfter(10) := eat.
_.actorOfEvent(eat) := mick.
_.objectOfEvent(eat) := food.
文件state2.pl:
:- module(state2, [isTime/2, injestEvent/2, justAfter/3,
actorOfEvent/3, objectOfEvent/3]).
:- reexport(logic).
_.isTime() := 20.
_.injestEvent() := sleep.
_.justAfter(20) := sleep.
_.actorOfEvent(sleep) := everyone.
_.objectOfEvent(sleep) := dream.
文件逻辑。
:- module(logic, [inside/4]).
M.inside(Food,Eater) := T :-
T = M.isTime(),
InjEvent = M.injestEvent(),
InjEvent = M.justAfter(T),
Eater = M.actorOfEvent(InjEvent),
Food = M.objectOfEvent(InjEvent).
要使逻辑在状态1和状态2中也可见,请使用reexport / 1。这允许将消息发送到state1或state2,但是仍然会处理逻辑方法。这是一个示例运行:
Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.19)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
?- T = state1{}.inside(Food,Eater).
T = 10,
Food = food,
Eater = mick.
?- T = state2{}.inside(Food,Eater).
T = 20,
Food = dream,
Eater = everyone.
当我们已经知道('。')/ 3呼叫站点时,isTime / 2,injestEvent / 2等的导出将与即将发布的Jekejeke Prolog 1.3.1版本一起消失。但是Jekejeke Prolog的结果是相同的:
Jekejeke Prolog 3, Runtime Library 1.3.0
(c) 1985-2018, XLOG Technologies GmbH, Switzerland
?- T = state1{}.inside(Food,Eater).
T = 10,
Food = food,
Eater = mick
?- T = state2{}.inside(Food,Eater).
T = 20,
Food = dream,
Eater = everyone