简体   繁体   English

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

[英]How do I interact with a Perl object that has a hash attribute?

I have a class with several variables, one of which is a hash (_runs): 我有一个包含多个变量的类,其中一个是散列(_runs):

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

Now, all I'm trying to do is create an accessor/mutator, as well as another subroutine that pushes new data into the hash. 现在,我要做的就是创建一个访问器/ mutator,以及另一个将新数据推入哈希的子例程。 But I'm having a hell of a time getting all the referencing/dereferencing/$self calls working together. 但我有一段时间让所有引用/解除引用/ $ self调用一起工作。 I've about burned my eyes out with "Can't use string ("blah") as a HASH ref etc etc" errors. 我用“不能使用字符串(”blah“)作为HASH ref等等”烧毁我的眼睛“错误。

For the accessor, what is 'best practice' for returning hashes? 对于访问者来说,返回哈希的“最佳实践”是什么? Which one of these options should I be using (if any)?: 我应该使用以下哪个选项(如果有的话)?:

return $self->{_runs};
return %{ $self->{_runs} };
return \$self->{_runs};

Further, when I'm using the hash within other subroutines in the class, what syntax do I use to copy it? 此外,当我在类中的其他子例程中使用哈希时,我使用什么语法来复制它?

my @runs = $self->{_runs};
my @runs = %{ $self->{_runs} };
my @runs = $%{ $self->{_runs} };
my @runs = $$self->{_runs};

Same goes for iterating over the keys: 迭代键是一样的:

foreach my $dt (keys $self->{_runs})
foreach my $dt (keys %{ $self->{_runs} })

And how about actually adding the data? 那么实际添加数据呢?

$self->{_runs}{$dt} = $duration;
%{ $self->{_runs} }{$dt} = $duration;
$$self->{_runs}{$dt} = $duration;

You get the point. 你明白了。 I've been reading articles about using classes, and articles about referencing and dereferencing, but I can't seem to get my brain to combine the knowledge and use both at the same time. 我一直在阅读有关使用类和有关引用和解除引用的文章的文章,但我似乎无法让我的大脑结合知识并同时使用它们。 I got my _times array working finally, but mimicking my array syntax over to hashes didn't work. 我最终得到了我的_times数组,但是模仿我的数组语法到哈希没有用。

You are storing references to array or hashes in your object. 您正在对象中存储对数组或哈希的引用。 To use them with standard functions you'll need to dereference them. 要将它们与标准函数一起使用,您需要取消引用它们。 For example: 例如:

@{ $self->{_array_ref_key} }; 
%{ $self->{_hash_ref_key} };

If you need pass parameters to standard function: 如果需要将参数传递给标准函数:

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
}

Also $self->{_hash_ref_key}{$hash_key} syntax is shortcut for $self->{_hash_ref_key}->{$hash_key} (which can make for sense if you see it first time). 此外, $self->{_hash_ref_key}{$hash_key}语法是$self->{_hash_ref_key}->{$hash_key}快捷方式(如果您第一次看到它,可能$self->{_hash_ref_key}->{$hash_key}意义)。

Also take a look at corresponding manual page . 另请查看相应的手册页

Might as well take my comments and make a proper answer out of it. 不妨接受我的评论,并从中做出正确的答案。 I'll illustrate exactly why your sample code failed. 我将准确说明您的示例代码失败的原因。

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,
}, '…' );

All the elements in the list form the key/value pairs for the hash whose reference is going to be bless ed. 列表中的所有元素形成哈希的键/值对,其引用将被bless () is an empty list, so what you're really expressing is the list '_name', $name, '_runs', '_times', [] . ()是一个空列表,所以你真正表达的是列表'_name', $name, '_runs', '_times', [] You can see that _times moves up to become a value, and the reference [] is stringified as hash key. 您可以看到_times向上移动成为值,并且引用[]被字符串化为散列键。 You get the warning because there's no value left for it; 你得到了警告,因为它没有任何价值; this will be automatically coerced to undef . 这将自动强制为undef (Always always enable the warnings pragma.) (始终始终启用warnings编译指示。)

Now for the guts part: hash values must be a scalar value. 现在对于guts部分:哈希值必须是标量值。 Arrays and hashes aren't; 数组和散列不是; but references to them are. 但是对它们的引用是。 Thus: 从而:

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

First, you have to figure out what you actually want to return and what you want the higher level to be able to do with the data. 首先,您必须弄清楚您实际想要返回的内容以及您希望更高级别能够处理数据的内容。

If you want to return a copy of the data or any changes to the returned data don't affect the copy in the object, you can't do the simple solutions that the other answers tell you because they return shallow copies which will still share internal references. 如果要返回数据的副本或对返回的数据的任何更改不影响对象中的副本,则不能执行其他答案告诉您的简单解决方案,因为它们返回仍将共享的浅副本内部参考。 You need to make a deep copy then return the disconnected data structure. 您需要进行深层复制,然后返回断开连接的数据结构。 Storable makes this easy with dclone : 可存储使这个容易dclone

 use Storable qw( dclone );

 sub some_method {
      my( $self, ... ) = @_;
      ...;
      my $clone = dclone( $self->{_runs} );
      $clone;
      }

If you want the higher level to change the object by changing the returned data structure, just return the reference that you already store. 如果希望更高级别通过更改返回的数据结构来更改对象,只需返回已存储的引用。 You don't need to do anything fancy for that: 你不需要做任何花哨的事情:

 sub some_method {
      my( $self, ... ) = @_;
      ...;
      $self->{_runs};
      }

Beyond that, it's your job to create an interface so that people don't have to think about your data structure at the higher level. 除此之外,创建一个界面是你的工作,这样人们就不必在更高层次上考虑你的数据结构。 You encapsulate everything so your implementation details don't show themselves. 封装了所有内容,因此您的实现细节不会显示出来。 That way, you can change the implementation without disturbing the higher level code (as long as the interface is stable). 这样,您可以更改实现而不会干扰更高级别的代码(只要接口稳定)。

You create a runs method that returns a list of runs: 您创建一个runs ,返回运行的列表方法:

 sub get_run_keys {
      my( $self ) = @_;

      keys %{ $self->{_runs} };
      }

Or maybe you just want the values: 或者你可能只想要这些值:

 sub get_run_values {
      my( $self ) = @_;

      values %{ $self->{_runs} };
      }

Or maybe the whole thing: 或许整件事:

 sub get_run_hash {
      my( $self ) = @_;

      $self->{_runs}; # subject to the cloning stuff I mentioned earlier
      }

When you want to get the values for a particular run, you access it through another method: 如果要获取特定运行的值,可以通过另一种方法访问它:

 sub get_run {
      my( $self, $key ) = @_;

      $self->{_runs}{$key};
      }

Setting a run value is similar: 设置运行值类似:

 sub set_run {
      my( $self, $key, $value ) = @_;

      $self->{_runs}{$key} = $value;
      }

Now your higher level doesn't know anything about the infrastructure, and the method names describe what you are trying to do instead of how the infrastructure has to do it: 现在,您的更高级别对基础架构一无所知,方法名称描述了您尝试执行的操作,而不是基础架构如何执行此操作:

 foreach my $key ( $self->get_run_keys ) {
     my $run = $self->get_run( $key );
     ...;
     $self->set_run( $key, $new_value );
     }

Object-oriented design is a big topic, and there is a lot you can do. 面向对象的设计是一个很大的主题,你可以做很多事情。 This is just enough to get you started. 这足以让你入门。 You can wrap other operations too: 您也可以包装其他操作:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM