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

时间:2010-05-26 15:08:46

标签: perl hash object

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

sub new
{
    my ($class, $name) = @_;
    my $self = {
        _name => $name,
        ...
        _runs => (),
        _times => [],
        ...
    };
    bless ($self, $class);
    return $self;
}

现在,我要做的就是创建一个accessor / mutator,以及另一个将新数据推送到hash中的子例程。但我有一段时间让所有引用/解除引用/ $ self调用一起工作。我用“不能使用字符串(”blah“)作为HASH参考等等”烧毁我的眼睛“错误。

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

return $self->{_runs};
return %{ $self->{_runs} };
return \$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数组,但是模仿我的数组语法到哈希不起作用。

3 个答案:

答案 0 :(得分:6)

您正在对象中存储对数组或哈希的引用。要将它们与标准函数一起使用,您需要取消引用它们。例如:

@{ $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_ref_key}{$hash_key}语法是$self->{_hash_ref_key}->{$hash_key}的快捷方式(如果您第一次看到它,这可能会有意义。)

另请查看corresponding manual page

答案 1 :(得分:5)

不妨接受我的评论,并从中做出正确的答案。我将准确说明您的示例代码失败的原因。

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编辑。 ()是一个空列表,所以你真正表达的是列表'_name', $name, '_runs', '_times', []。您可以看到_times向上移动成为值,并且引用[]被字符串化为散列键。你得到了警告,因为它没有任何价值;这将自动强制转移到undef。 (始终始终启用warnings编译指示。)

现在对于guts部分:哈希值必须是标量值。数组和散列不是;但是对它们的引用是。因此:

my $self = {
    _name => $name,
    _runs => {},
    _times => [],
};

答案 2 :(得分:3)

首先,您必须弄清楚您实际想要返回的内容以及您希望更高级别能够处理数据的内容。

如果要返回数据副本或对返回数据的任何更改不影响对象中的副本,则无法执行其他答案告诉您的简单解决方案,因为它们返回浅拷贝仍将共享内部参考。您需要进行深层复制,然后返回断开连接的数据结构。使用dclone

Storable可以轻松实现这一目标
 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} = {};
      }