Logtalk:meta::map,lambda表达式和对私有方法的访问



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

:- 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的使用将事实放在了正确的命名空间中(在新创建的实例上)。然而,如果我机智地用这个lambda表达式替换process_arguments/1的主体:

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作为一个公共规则,而我不愿意这样做。我错过了什么?

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

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

:- 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开发版本中。谢谢你引起我对这个bug的关注。

相关内容

  • 没有找到相关文章

最新更新