【问题标题】:How do I interact with a Perl object that has a hash attribute?如何与具有哈希属性的 Perl 对象交互?
【发布时间】:2010-05-26 15:08:45
【问题描述】:

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

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

现在,我要做的就是创建一个访问器/修改器,以及另一个将新数据推送到散列中的子例程。但是我有一个地狱般的时间让所有的引用/取消引用/$self 调用一起工作。 “不能使用字符串 ("blah") 作为 HASH ref 等”错误,我的眼睛都快烧坏了。

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

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 数组工作了,但是将我的数组语法模仿成哈希并没有用。

【问题讨论】:

  • _runs => (), 这已经是错误了,这一定是一个hashref。 _runs => { foo => 1, bar => 2, },
  • 但我在调用构造函数时不知道任何键,我需要一个空哈希
  • 你需要一个空的 hashref。 _runs => {},

标签: perl hash object


【解决方案1】:

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

@{ $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

【讨论】:

  • 谢谢,这与上面 daxim 的评论相结合正是我所需要的。
【解决方案2】:

还不如把我的 cmets 拿出来做一个正确的答案。我将准确说明您的示例代码失败的原因。

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

列表中的所有元素构成哈希的键/值对,其引用将是blessed。 () 是一个空列表,所以你真正表达的是列表'_name', $name, '_runs', '_times', []。您可以看到_times 向上移动成为一个值,并且引用[] 被字符串化为哈希键。您收到警告是因为它没有任何价值;这将被自动强制转换为undef。 (始终启用 warnings 杂注。)

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

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

【讨论】:

  • 非常有用,感谢您的帮助。我只知道我通常将哈希初始化为“%var = ()”,所以我假设(我知道,我知道)构造函数应该使用相同的东西。不要以为我自己能找到,再次感谢。
  • 您只使用my %var; 声明哈希。分配一个空列表是完全多余的。
【解决方案3】:

首先,您必须弄清楚您真正想要返回什么,以及您希望更高级别能够对数据执行什么操作。

如果您想返回数据的副本或对返回数据的任何更改都不会影响对象中的副本,则您无法执行其他答案告诉您的简单解决方案,因为它们返回 浅拷贝仍将共享内部引用。您需要制作一个深拷贝,然后返回断开的数据结构。 Storable 使用 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} = {};
      }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-05
    • 2021-01-22
    • 1970-01-01
    • 1970-01-01
    • 2014-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多