如何与具有哈希属性的Perl对象交互?

如何与具有哈希属性的Perl对象交互?,perl,hash,object,Perl,Hash,Object,我有一个包含多个变量的类,其中一个是散列(\u运行): 现在,我要做的就是创建一个访问器/变异器,以及另一个将新数据推送到散列中的子例程。但是我在让所有的引用/取消引用/$self调用一起工作时经历了一段痛苦的时光。我几乎被“不能使用字符串(“blah”)作为散列引用等”错误弄得眼花缭乱 对于访问器,返回哈希的“最佳实践”是什么?我应该使用这些选项中的哪一个(如果有)?: 此外,当我在类中的其他子例程中使用哈希时,我应该使用什么语法来复制它 my @runs = $self->{_runs

我有一个包含多个变量的类,其中一个是散列(\u运行):

现在,我要做的就是创建一个访问器/变异器,以及另一个将新数据推送到散列中的子例程。但是我在让所有的引用/取消引用/$self调用一起工作时经历了一段痛苦的时光。我几乎被“不能使用字符串(“blah”)作为散列引用等”错误弄得眼花缭乱

对于访问器,返回哈希的“最佳实践”是什么?我应该使用这些选项中的哪一个(如果有)?:

此外,当我在类中的其他子例程中使用哈希时,我应该使用什么语法来复制它

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} = {};
      }