SWI-Prolog中的面向对象编程

use*_*815 13 oop module object prolog swi-prolog

我在某处读到你可以将模块视为Prolog中的对象.我试图解决这个问题,如果这是一个很好的编码方式.

如果我有两个文件,一个定义一个类狗,然后另一个使用这个类来生成两个狗对象.

:- module(dog,
      [ create_dog/4,bark/1 ]).

create_dog(Name,Age,Type,Dog):-
   Dog = dog(name(Name),age(Age),type(Type)).

bark(Dog):-
   Dog = dog(name(_Name),age(_Age),type(Type)),
   Type = bassethound,
   woof.
bark(Dog):-
   Dog = dog(name(_Name),age(_Age),type(Type)),
   Type \= bassethound,
   ruff.

woof:-format("woof~n").

ruff:-format("ruff~n").
Run Code Online (Sandbox Code Playgroud)

第二个档案

use_module(library(dog)).

run:-
   dog:create_dog('fred',5,bassethound,Dog),
   forall(between(1,5,_X),
       dog:bark(Dog)
      ),
   dog:create_dog('fido',6,bloodhound,Dog2),
   dog:bark(Dog2).
Run Code Online (Sandbox Code Playgroud)

这使得一只狗对象Dog是一只巴塞特猎犬并且让它吠5次,然后我再制作另一只狗对象Dog2,它是一只猎犬并使它也吠叫.我知道在oop中你有对象有行为和状态.所以我现在根据自己的状态有两个具有不同行为的对象,但目前我将对象的状态存储在Dog变量中,主程序中的代码可以看到它们.有没有办法隐藏对象的状态,即拥有私有变量?例如,我可能希望有一种方法可以为每个狗对象存储状态has_barked,如果它在程序中较早出现,则为true,否则为false,然后bark/1根据此更改行为.

您还将如何处理继承和覆盖方法等?任何指向读数的指针都欢迎.谢谢.

Pau*_*ura 7

只是Logtalk中可能重新实现示例代码之一的示例.它使用原型来简化,但它仍然说明了一些关键概念,包括继承,默认谓词定义,静态和动态对象以及参数对象.

% a generic dog
:- object(dog).

    :- public([
        create_dog/3, bark/0, name/1, age/1
    ]).

    create_dog(Name, Age, Dog) :-
        self(Type),
        create_object(Dog, [extends(Type)], [], [name(Name),age(Age)]).

    % default definition for all dogs
    bark :-
        write(ruff), nl.

:- end_object.


:- object(bassethound,
    extends(dog)).

    % bark different
    bark :-
        write(woof), nl.

:- end_object.


:- object(bloodhound,
    extends(dog)).

:- end_object.


% support representing dogs as plain database facts using a parametric object
:- object(dog(_Name,_Age,_Type),
    extends(dog)).

    name(Name) :-
        parameter(1, Name).

    age(Age) :-
        parameter(2, Age).

    bark :-
        parameter(3, Type),
        [Type::bark].

:- end_object.


% a couple of (static) dogs as parametric object proxies
dog(fred, 5, bassethound).
dog(fido, 6, bloodhound).


% another static object
:- object(frisbee,
    extends(bloodhound)).

    name(frisbee).
    age(1).

:- end_object.
Run Code Online (Sandbox Code Playgroud)

一些示例查询:

$ swilgt
...
?- {dogs}.
% [ /Users/foo/dogs.lgt loaded ]
% (0 warnings)
true.

?- bassethound::bark.
woof
true.

?- bloodhound::bark.
ruff
true.

?- bassethound::create_dog(boss, 2, Dog).
Dog = o1.

?- o1::bark.
woof
true.

?- {dog(Name, Age, Type)}::bark.
woof
Name = fred,
Age = 5,
Type = bassethound ;
ruff
Name = fido,
Age = 6,
Type = bloodhound.

?- dog(ghost, 78, bloodhound)::(bark, age(Age)).
ruff
Age = 78.

?- forall(between(1,5,_X), {dog(fred,_,_)}::bark).
woof
woof
woof
woof
woof
true.
Run Code Online (Sandbox Code Playgroud)

一些笔记.::/2是消息发送控件构造.目标{Object}::Message只是证明Object使用普通的Prolog数据库,然后将消息发送Message到结果.目标在保留原始发件人的同时将消息[Object::Message] 委托给对象.


Pau*_*ura 5

Prolog 模块可以简单地解释为对象(特别是原型)。Prolog 模块可以动态创建,具有可以视为其身份的名称(因为它在运行会话中必须是唯一的,因为模块命名空间是扁平的),并且可以具有动态状态(使用模块本地的动态谓词)。然而,在大多数系统中,它们提供弱封装,因为您通常可以使用显式限定调用任何模块谓词(也就是说,至少有一个系统 ECLiPSe 允许您锁定模块以防止以这种方式破坏封装)。也不支持将接口与实现分离或具有同一接口的多个实现(您可以以某种方式破解它,这取决于 Prolog 模块系统,但它并不漂亮)。

正如其他答案中提到的,Logtalk 是 Prolog 的高度可移植的面向对象扩展,支持大多数系统,包括 SWI-Prolog。Logtalk 对象从概念和实践的角度都包含 Prolog 模块。Logtalk 编译器支持模块功能的通用核心。例如,您可以使用它在没有模块系统的情况下在 Prolog 实现中编写模块代码。Logtalk 可以将模块编译为对象,并支持对象和模块之间的双向调用。

请注意,逻辑编程中的对象最好被视为一种代码封装和代码重用机制。就像模块一样。OO 概念可以(并且已经)成功应用于其他编程范式,包括功能和逻辑。但这并不意味着一定要带来命令式/过程式概念。例如,实例与其之间或原型其父之间的关系可以解释为指定代码重用模式 而不是从动态/状态的角度来看(实际上,在从命令式/过程式语言派生的 OOP 语言中,实例只不过是一种美化的动态数据结构,其规范分布在其类和类的超类之间)。

考虑到您的示例代码,您可以在接近您的公式的 Logtalk 中轻松重新编码,但也可以通过其他方式轻松重新编码,其中最有趣的是不使用动态功能。存储状态(如在动态状态中)有时是必要的,甚至可能是特定问题的最佳解决方案(Prolog 有动态谓词是有原因的!)但应谨慎使用,并且仅在真正必要时使用。使用 Logtalk 不会改变(或打算改变)这一点。

我建议您查看广泛的 Logtalk 文档及其众多的编程示例。在那里,您会发现如何将接口与实现完全分离,如何使用组合、继承、专门化或覆盖继承的谓词等。

  • 几年前,我尝试按照[文档](http://www.swi-prolog.org/pldoc/man?section=dynamic-modules)将“动态模块”实现转化为实际用途,但我有过一次糟糕的经历,所以我不想向OP建议这种可能性。 (2认同)