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
根据此更改行为.
您还将如何处理继承和覆盖方法等?任何指向读数的指针都欢迎.谢谢.
只是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]
委托给对象.
Prolog 模块可以简单地解释为对象(特别是原型)。Prolog 模块可以动态创建,具有可以视为其身份的名称(因为它在运行会话中必须是唯一的,因为模块命名空间是扁平的),并且可以具有动态状态(使用模块本地的动态谓词)。然而,在大多数系统中,它们提供弱封装,因为您通常可以使用显式限定调用任何模块谓词(也就是说,至少有一个系统 ECLiPSe 允许您锁定模块以防止以这种方式破坏封装)。也不支持将接口与实现分离或具有同一接口的多个实现(您可以以某种方式破解它,这取决于 Prolog 模块系统,但它并不漂亮)。
正如其他答案中提到的,Logtalk 是 Prolog 的高度可移植的面向对象扩展,支持大多数系统,包括 SWI-Prolog。Logtalk 对象从概念和实践的角度都包含 Prolog 模块。Logtalk 编译器支持模块功能的通用核心。例如,您可以使用它在没有模块系统的情况下在 Prolog 实现中编写模块代码。Logtalk 可以将模块编译为对象,并支持对象和模块之间的双向调用。
请注意,逻辑编程中的对象最好被视为一种代码封装和代码重用机制。就像模块一样。OO 概念可以(并且已经)成功应用于其他编程范式,包括功能和逻辑。但这并不意味着一定要带来命令式/过程式概念。例如,实例与其类之间或原型与其父类之间的关系可以解释为指定代码重用模式 而不是从动态/状态的角度来看(实际上,在从命令式/过程式语言派生的 OOP 语言中,实例只不过是一种美化的动态数据结构,其规范分布在其类和类的超类之间)。
考虑到您的示例代码,您可以在接近您的公式的 Logtalk 中轻松重新编码,但也可以通过其他方式轻松重新编码,其中最有趣的是不使用动态功能。存储状态(如在动态状态中)有时是必要的,甚至可能是特定问题的最佳解决方案(Prolog 有动态谓词是有原因的!)但应谨慎使用,并且仅在真正必要时使用。使用 Logtalk 不会改变(或打算改变)这一点。
我建议您查看广泛的 Logtalk 文档及其众多的编程示例。在那里,您会发现如何将接口与实现完全分离,如何使用组合、继承、专门化或覆盖继承的谓词等。