6

在我们的类中,我们有一个模式,我们创建一个属性来表示计算值。出于显而易见的原因,我们希望缓存计算的值,然后在基础值之一更改时使缓存无效。

所以我们目前有这个:

package FooBar;
use Moose;

has 'foo' => (
        accessor => {
            'foo' => sub {
                my $self = shift;
                if (@_ > 0) {
                    # writer
                    $self->{foo} = $_[0];

      # reset fields that are dependant on me
      $self->{bar} = undef;
                }
                # reader part;
                return $self->{foo};
            }
        }
    );

has 'bar' => (
        accessor => {
            'bar' => sub {
                my $self = shift;
                if (@_ > 0) {
                    # writer
                    $self->{bar} = $_[0];
                }
                # reader part;
                $self->{bar} = calculate_bar($self->foo, $self->baz) 
                        if (not defined($self->{bar}));
                return $self->{bar};
            }
        }
    );

sub calculate_bar { ... }

当计算值依赖于其他计算值时,这种长手方法变得非常繁琐且容易出错。

'bar' 是否有更智能/更简单的方法来监控它所依赖的属性,而不是让 'foo' 知道谁依赖它?另外如何避免通过哈希成员访问设置栏?

4

4 回答 4

11

如果我理解正确,您可以在设置触发器时使用触发器来清除属性。这是一个例子:

has 'foo' => (
    is      => 'rw',
    trigger => sub{
        my ($self) = @_;
        $self->clear_bar;
    }
);

has 'bar' => (
    is      => 'rw',
    clearer => 'clear_bar',
    lazy    => 1,
    default => sub{
        my ($self) = @_;
        return calculate_bar( ... );
    }
);

foo因此,对via的任何写入都$obj->foo($newvalue)将导致bar被清除,并在下次访问时重新创建。

于 2009-11-21T14:14:49.080 回答
4

我认为你很可能通过使用带有惰性的属性隐式记忆来让自己变得更难,当你可以让记忆显式使你的整个程序更加透明时

has [qw/foo bar baz/] => ( isa => 'Value', is => 'rw' );

use Memoize;
memoize('_memoize_this');

sub old_lazy_attr {
    my $self = shift;
    _memoize_this( $self->attr1, $self->attr2, $self->attr3 );
}

sub _memoize_this {
    my @args = @_;
    # complex stuff
    return $result
}

有关内部缓存的信息和控制,请参阅 cpan 的Memoize,还请记住,Memoized 函数不能依赖于对象的状态。所以参数必须显式传入

于 2009-11-21T20:20:39.387 回答
0

这行得通吗?

#!/usr/bin/perl

package Test;

use Modern::Perl;
use Moose;

has a => (is => 'rw', isa => 'Str', trigger => \&change_a);
has b => (is => 'rw', isa => 'Str', trigger => \&change_b);
has c => (is => 'rw', isa => 'Str');

sub change_a
{
    my $self = shift;
    say 'update b';
    $self->b($self->a . ', bar');
}   

sub change_b
{
    my $self = shift;
    say 'update c';
}   

package main;

my $test = Test->new->a('Foo');

输出:

$ perl test.pl
update b
update c
于 2009-11-21T14:14:53.183 回答
0

我还没有对 Moose 内部结构和元对象协议进行任何研究,但我认为现在是这样做的好时机。

您想要修补代码生成,以便当您将属性指定为

has 'foo' => ();
has 'bar' => ( 
    depends_on => [qw( foo )],
    lazy => \&calculate_bar,
);

代码生成阶段为您在上面指定的foobar属性创建代码。

如何做到这一点是留给读者的练习。如果我有线索,我会试着给你一个开始。不幸的是,我只能告诉你“这是 MOP 的工作”。

于 2009-11-23T01:21:25.620 回答