Prolog:覆盖谓词与使用谓词之间的区别

时间:2018-09-28 22:34:39

标签: object module prolog

我真的很愚蠢,觉得我想念一些东西。

我基本上有两个文件:

  • 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的本地定义将覆盖从模块的弱导入

我如何使用从模块导入的谓词,而不是重新定义它?

3 个答案:

答案 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