7

後で使用するためにいくつかの計算をキャッシュしたい状況があります。許可された値のリストがあるとしましょう。そのリストに何かが含まれているかどうかを確認するので、効率と利便性のためにハッシュとして使用したいと思います。そうでなければ、grepする必要があります。

Mooseを使用している場合は、許可される値のリストが変更されるたびにキャッシュが再計算されると便利です。私はそれをトリガーで簡単に行うことができます...

has allowed_values => (
    is          => 'rw',
    isa         => 'ArrayRef',
    trigger     => sub {
        my %hash = map { $_ => 1 } @{$_[1]};
        $_[0]->allowed_values_cache(\%hash);
    }
);

has allowed_values_cache => (
    is          => 'rw',
    isa         => 'HashRef',
);

そして、2つは同期を維持します...

$obj->allowed_values([qw(up down left right)]);
print keys %{ $obj->allowed_values_cache };   # up down left right

ここで、デフォルトの、十分に単純な変更が必要だとしましょうallowed_values...

has allowed_values => (
    is          => 'rw',
    isa         => 'ArrayRef',
    trigger     => sub {
        my %hash = map { $_ => 1 } @{$_[1]};
        $_[0]->allowed_values_cache(\%hash);
    },
    default     => sub {
        return [qw(this that whatever)]
    },
);

...デフォルトを設定することを除いて、トリガーは呼び出されません。DWIMに到達するには、キャッシュを複製する必要があります。

has allowed_values => (
    is          => 'rw',
    isa         => 'ArrayRef',
    trigger     => sub {
        $_[0]->cache_allowed_values($_[1]);    
    },
    default     => sub {
        my $default = [qw(this that whatever)];
        $_[0]->cache_allowed_values($default);
        return $default;
    },
);

sub cache_allowed_values {
    my $self = shift;
    my $values = shift;

    my %hash = map { $_ => 1 } @$values;
    $self->allowed_values_cache(\%hash);

    return;
}

Mooseのドキュメントは、デフォルトが設定されているときに呼び出されないことを明示してtriggerいますが、邪魔になります。私はそこでの複製が好きではありません。

それを行うためのより良い方法はありますか?

4

2 に答える 2

7

私は最近これに直面し、#mooseチャンネルで尋ねたところ、次のように処理するように言われました。

としてマークcache_allowed_valuesし、現在のlazy_build_build_cache_allowed_values参照しallowed_values、書き込みトリガーallowed_valuesをクリアしますcache_allowed_values

そうすれば、値が要求されたり保存されたりする順序に関係なく、最小限の作業で常に正しくなります。


例:

has cache_allowed_values => (is => 'ro', lazy_build => 1);
sub _build_cache_allowed_values {
  return { map { $_ => 1 } @{shift->allowed_values} };
}
has allowed_values => (
  is => 'rw',
  trigger => sub { shift->clear_cache_allowed_values },
  default => ...,
);
于 2009-10-11T15:40:27.973 に答える
2

私はあなたが本当にあなたが望むallowed_values効率と順序付けの特性を備えた別個のデータ構造になりたいと思うと思います。順序を気にしないように見えるので、次のことを行ってください。

has 'allowed_values' => (
    traits  => ['Hash'],
    isa     => HashRef[Bool],
    default => sub { +{} },
    handles => {
        _add_allowed_value   => 'set',
        remove_allowed_value => 'delete',
        value_is_allowed     => 'exists',
        allowed_values       => 'keys',
    },
);

method add_allowed_value(Str $value){
    $self->_add_allowed_value( $value, 1 );
}

一般に、実装されているクラスに固有でないものは、おそらく他の場所に実装する必要があります。配列のルックアップ時間を短縮することは、実際に作成しているクラスの仕事ではないため、他の場所に実装する必要があり、このクラスはそのクラスを使用する必要があります。(上記のハッシュのような単純なケースでは、このルールを無視してもかまいません。しかし、それがさらに複雑な場合は、間違いなく除外する必要があります。)

編集:

これがリストであるとユーザーに思わせたい場合は、次のようにします。

use MooseX::Types::Moose qw(Bool ArrayRef HashRef);
use MooseX::Types -declare => ['ListHash'];
subtype ListHash, as HashRef[Bool];

coerce ListHash, from ArrayRef, via { +{ map { $_ => 1 } @$_ } };

has 'allowed_values' => (
    # <same as above>
    isa    => ListHash,
    writer => 'set_allowed_values',
    coerce => 1,
);

allowed_valuesこれで、次のように設定できます。

my $instance = Class->new( allowed_values => [qw/foo bar/] );
$instance->set_allowed_values([qw/foo bar baz/]);

そして、次のようにそれらにアクセスします。

my @allowed_values = $instance->allowed_values;
... if $instance->value_is_allowed('foo');

そしてそれらを変更します:

$instance->remove_allowed_value('foo');
$instance->add_allowed_value('gorch');

これにより、基盤となる実装の詳細がユーザーから隠されます。

ところで、実際にハッシュを構築し、それを3つの要素に対する線形スキャンよりも大幅に高速に使用していますか?

于 2009-10-12T06:29:49.433 に答える