如何与具有哈希属性的Perl对象交互?
我有一个包含多个变量的类,其中一个是散列(\u运行): 现在,我要做的就是创建一个访问器/变异器,以及另一个将新数据推送到散列中的子例程。但是我在让所有的引用/取消引用/$self调用一起工作时经历了一段痛苦的时光。我几乎被“不能使用字符串(“blah”)作为散列引用等”错误弄得眼花缭乱 对于访问器,返回哈希的“最佳实践”是什么?我应该使用这些选项中的哪一个(如果有)?: 此外,当我在类中的其他子例程中使用哈希时,我应该使用什么语法来复制它如何与具有哈希属性的Perl对象交互?,perl,hash,object,Perl,Hash,Object,我有一个包含多个变量的类,其中一个是散列(\u运行): 现在,我要做的就是创建一个访问器/变异器,以及另一个将新数据推送到散列中的子例程。但是我在让所有的引用/取消引用/$self调用一起工作时经历了一段痛苦的时光。我几乎被“不能使用字符串(“blah”)作为散列引用等”错误弄得眼花缭乱 对于访问器,返回哈希的“最佳实践”是什么?我应该使用这些选项中的哪一个(如果有)?: 此外,当我在类中的其他子例程中使用哈希时,我应该使用什么语法来复制它 my @runs = $self->{_runs
my @runs = $self->{_runs};
my @runs = %{ $self->{_runs} };
my @runs = $%{ $self->{_runs} };
my @runs = $$self->{_runs};
对键进行迭代也是如此:
foreach my $dt (keys $self->{_runs})
foreach my $dt (keys %{ $self->{_runs} })
那么实际添加数据呢
$self->{_runs}{$dt} = $duration;
%{ $self->{_runs} }{$dt} = $duration;
$$self->{_runs}{$dt} = $duration;
你明白了。我一直在阅读关于使用类的文章,以及关于引用和取消引用的文章,但我似乎无法让我的大脑同时将知识和使用两者结合起来。我终于让我的_times数组工作了,但是把我的数组语法模仿成散列没有用。您正在对象中存储对数组或散列的引用。要将它们与标准函数一起使用,您需要取消对它们的引用。例如:
@{ $self->{_array_ref_key} };
%{ $self->{_hash_ref_key} };
如果需要将参数传递给标准函数:
push( @{ $self->{_array_ref_key} }, $some_value );
for my $hash_key ( keys %{ $self->{_hash_ref_key} }) {
$self->{_hash_ref_key}{$hash_key}; ## you can access hash value by reference
}
另外,$self->{$hash\u ref\u key}{$hash\u key}
语法是$self->{$hash\u ref\u key}->{$hash\u key}
的快捷方式(如果您第一次看到它,这可能会有意义)
还可以看一看。不妨听听我的意见,并从中做出正确的回答。我将详细说明示例代码失败的原因
use warnings;
my $self = {
_name => $name,
_runs => (),
_times => [],
};
bless ($self, $class);
use Data::Dump::Streamer; DumpLex $self;
__END__
Odd number of elements in anonymous hash at …
$self = bless( {
_name => undef,
_runs => '_times',
"ARRAY(0x88dcb8)" => undef,
}, '…' );
列表中的所有元素构成散列的键/值对,其引用将是bless
ed。()
是一个空列表,因此您真正要表达的是列表“\u name',$name'”\u runs',“\u times',[]
。您可以看到,\u times
向上移动成为一个值,引用[]
被字符串化为散列键。你得到警告是因为它没有价值了;这将自动强制为undef
。(始终启用警告
杂注。)
现在来看guts部分:散列值必须是标量值。数组和散列不是;但对它们的引用是错误的。因此:
my $self = {
_name => $name,
_runs => {},
_times => [],
};
首先,您必须弄清楚您实际想要返回什么,以及您希望更高级别能够对数据执行什么操作 如果要返回数据的副本,或者对返回数据的任何更改都不会影响对象中的副本,则不能执行其他答案告诉您的简单解决方案,因为它们返回仍将共享内部引用的浅副本。您需要进行深度复制,然后返回断开连接的数据结构。使用
dclone
可以轻松实现这一点:
use Storable qw( dclone );
sub some_method {
my( $self, ... ) = @_;
...;
my $clone = dclone( $self->{_runs} );
$clone;
}
如果希望更高级别通过更改返回的数据结构来更改对象,只需返回已存储的引用即可。你不需要为此做任何花哨的事情:
sub some_method {
my( $self, ... ) = @_;
...;
$self->{_runs};
}
除此之外,您的工作是创建一个接口,这样人们就不必在更高的层次上考虑您的数据结构。您封装了所有内容,因此您的实现细节不会显示出来。这样,您就可以在不干扰更高级别代码的情况下更改实现(只要接口稳定)
创建一个runs
方法,该方法返回运行列表:
sub get_run_keys {
my( $self ) = @_;
keys %{ $self->{_runs} };
}
或者,您可能只需要以下值:
sub get_run_values {
my( $self ) = @_;
values %{ $self->{_runs} };
}
或者整个事情:
sub get_run_hash {
my( $self ) = @_;
$self->{_runs}; # subject to the cloning stuff I mentioned earlier
}
如果要获取特定运行的值,可通过另一种方法进行访问:
sub get_run {
my( $self, $key ) = @_;
$self->{_runs}{$key};
}
设置运行值类似于:
sub set_run {
my( $self, $key, $value ) = @_;
$self->{_runs}{$key} = $value;
}
现在,您的上级对基础架构一无所知,方法名称描述了您试图做什么,而不是基础架构必须如何做:
foreach my $key ( $self->get_run_keys ) {
my $run = $self->get_run( $key );
...;
$self->set_run( $key, $new_value );
}
面向对象设计是一个大话题,你可以做很多事情。这就足够让你开始了。您也可以包装其他操作:
sub does_run_exist {
my( $self, $key ) = @_;
exists $self->{_runs}{$key};
}
sub delete_runs {
my( $self, @keys ) = @_;
delete $self->{_runs}{$key} foreach my $keys ( @keys );
}
sub reset_runs {
my( $self, $key ) = @_;
$self->{_runs} = {};
}
\u runs=>(),
这已经是错误了,这一定是一个hashref<代码>\u runs=>{foo=>1,bar=>2,},但是我在调用构造函数时不知道任何键,我需要一个空hashref<代码>\u runs=>{},谢谢,这与上面daxim的评论相结合正是我所需要的。非常有用,谢谢你的帮助。我只知道我通常将哈希初始化为“%var=()”,所以我假设(我知道,我知道)构造函数应该使用相同的东西。不要以为我自己就可以找到,再次感谢。你用my%var代码>。分配一个空列表是完全多余的。
foreach my $key ( $self->get_run_keys ) {
my $run = $self->get_run( $key );
...;
$self->set_run( $key, $new_value );
}
sub does_run_exist {
my( $self, $key ) = @_;
exists $self->{_runs}{$key};
}
sub delete_runs {
my( $self, @keys ) = @_;
delete $self->{_runs}{$key} foreach my $keys ( @keys );
}
sub reset_runs {
my( $self, $key ) = @_;
$self->{_runs} = {};
}