Moose:属性值更改时缓存的计算结果会过期吗?

发布于 2024-08-11 11:58:54 字数 1183 浏览 8 评论 0原文

在我们的类中,我们有一个模式,我们创建一个属性来表示 计算值。出于显而易见的原因,我们想要缓存计算值 然后当基础值之一发生变化时使缓存失效。

所以我们目前有这样的情况:

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”知道谁依赖它?另外我怎样才能避免通过哈希设置栏 会员访问?

In our classes we have a pattern where we create an attribute to represent a
calculated value. For obvious reasons we want to cache the calculated value
and then invalidate the cache when one of the underlying values change.

So we currently have this:

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 { ... }

This long hand method is getting very tedious and error prone when calculated values
depend on other calculated values.

Is there a smarter/simpler way for 'bar' to monitor the attributes it depends on
vs having 'foo' know who is dependent on it? Also how can I avoid setting bar via hash
member access?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

逆蝶 2024-08-18 11:58:54

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

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( ... );
    }
);

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

If I understand you correctly, you can use triggers to clear attributes when one is set. Here's an example:

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( ... );
    }
);

So, any writes to foo via $obj->foo($newvalue) will cause bar to be cleared, and recreated on next access.

迷鸟归林 2024-08-18 11:58:54

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

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 函数不能依赖于对象的状态。因此参数必须显式传递

I think it is quite possible that you're making this harder on yourself by using an Attributes implicit memoization with lazy, when you could just make the memoization explicit making your whole program more transparent

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
}

See cpan's Memoize for information and control of the internal cache, also remember that a Memoized function can not be dependent on the state of the object. So the arguments must be passed in explicitly.

迟月 2024-08-18 11:58:54

这行得通吗?

#!/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

Would this work?

#!/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');

Output:

$ perl test.pl
update b
update c
不离久伴 2024-08-18 11:58:54

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

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

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

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

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

I haven't done any poking around in Moose internals and the meta object protocol, but I think this is a good time to do it.

You want to patch the code generation so that when you specify an attribute as

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

the code generation phase creates code for the foo and bar attributes as you specified above.

How to do this is an exercise left to the reader. If I had a clue, I'd try to give you a start. Unfortunately, all I can advise you with is "This is a job for the MOP".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文