2

我认为这是一个范围相关的问题。如果我对我的对象有这样的规则:

:- public(new/2).
:- mode(new(+list, -object_identifier), one).
new(Args, Instance) :-
    self(Self),
    create_object(Instance, [instantiates(Self)], [], []),
    Instance::process_arguments(Args).

如果我跳这个舞,我觉得这很好用:

:- object(name, instantiates(name)).

我不完全理解为什么这是必要的,但我怀疑它与我的实际问题有关,即如果我的对象中有标准 Prolog 循环,如下所示:

process_arguments([Arg|Args]) :- process_arg(Arg), process_arguments(Args).
process_arguments([]).

process_arg(Arg) :- ::asserta(something(Arg)).

我发现这种使用::asserta将事实放在正确的名称空间中(在新创建的实例上)。但是,如果我机智地process_arguments/1用这个 lambda 表达式替换 的主体:

process_arguments(Args) :- meta::map([Arg]>>process_arg(Arg), Args).

然后我将事实添加到父类并由所有实例共享。如果我用这个替换它:

process_arguments(Args) :-
    self(Self),
    meta::map([Arg]>>(Self::process_arg(Arg)), Args).

process_arg/1然后它会起作用,但是当我不想这样做时,我必须制定一条公共规则。我错过了什么?

4

1 回答 1

1

让我首先从上面的代码片段开始,对象name实例化自己。在这样做时,您创建name了自己的类。哪里不对。在支持元类的语言中,例如 Smalltalk 和 Logtalk,使一个类成为自己的元类是避免无限回归的经典方法。例如,参见元类 ( http://en.wikipedia.org/wiki/Metaclass ) 的维基百科条目。另请参阅 Logtalk 发行版中的“反射”示例。通过使对象name实例化自身,它既扮演了实例的角色(因为它实例化了一个对象),又扮演了一个类的角色(因为它被一个对象实例化了)。如果您定义name为独立对象,即与其他对象没有关系的对象,它将被编译为原型。

现在回答你的问题。在 Logtalk 中,元谓词(例如)在发送者meta::map/2的上下文中被调用。如果谓词在 中定义,则执行上下文(包括self的值)将为。因此,子句 for将在 中断言。您的解决方法(通过使用内置方法)按预期工作,但它确实强制您声明公共谓词。这是稳定的 Logtalk 版本中的一个错误,因为它也应该通过声明谓词 protected 或 private 来工作(因为sender是并且谓词在sender中声明)。例如:process_arguments/1namenamesomething/1nameself/1process_arg/1process_arg/1name

:- object(name,
    instantiates(name)).

    :- public(new/2).
    :- mode(new(+list, -object_identifier), one).
    new(Args, Instance) :-
        self(Self),
        create_object(Instance, [instantiates(Self)], [set_logtalk_flag(dynamic_declarations, allow)], []),
        meta::map({Instance}/[Arg]>>(Instance::process_arg(Arg)), Args).

    :- private(process_arg/1).
    process_arg(Arg) :-
        ::asserta(something(Arg)).

:- end_object.

我将在本周晚些时候将错误修复推送到公开可用的 Logtalk 开发版本中。感谢您引起我对这个错误的关注。

于 2012-02-27T22:55:20.967 回答