2

在 Perl 中,我正在学习如何取消引用“子例程引用”。但我似乎无法将子例程引用用作哈希“键”。

在以下示例代码中,

  1. 我可以创建对子例程 ($subref) 的引用,然后取消引用它以运行子例程 (&$subref)
  2. 我可以将引用用作哈希“值”,然后轻松取消引用
  3. 但我无法弄清楚如何将引用用作哈希“键”。当我从散列中取出密钥时,Perl 将密钥解释为字符串值(不是引用)——我现在明白了(感谢这个网站!)。所以我尝试了 Hash::MultiKey,但这似乎把它变成了一个数组引用。我想把它当作一个子程序/代码参考,假设这是可能的?

还有其他想法吗?

use strict;
#use diagnostics;
use Hash::MultiKey;    

my $subref = \&hello;

#1: 
&$subref('bob','sue');               #okay

#2:
my %hash;
$hash{'sayhi'}=$subref;
&{$hash{'sayhi'}}('bob','sue');      #okay

#3: 
my %hash2;
tie %hash2, 'Hash::MultiKey';
$hash2{$subref}=1;
foreach my $key (keys %hash2) {
  print "Ref type is: ". ref($key)."\n";
  &{$key}('bob','sue');              # Not okay 
}

sub hello {
    my $name=shift;
    my $name2=shift;
    print "hello $name and $name2\n";
}

这是返回的内容:

hello bob and sue
hello bob and sue
Ref type is: ARRAY
Not a CODE reference at d:\temp\test.pl line 21.
4

3 回答 3

2

没错,普通的哈希键只是一个字符串。不是字符串的东西会被强制转换为它们的字符串表示。

my $coderef = sub { my ($name, $name2) = @_; say "hello $name and $name2"; };
my %hash2 = ( $coderef => 1, );
print keys %hash2;  # 'CODE(0x8d2280)'

Tie ing 是修改该行为的常用方法,但Hash::MultiKey对您没有帮助,它有不同的目的:顾名思义,您可能有多个键,但同样只有简单的字符串:

use Hash::MultiKey qw();
tie my %hash2, 'Hash::MultiKey';
$hash2{ [$coderef] } = 1;
foreach my $key (keys %hash2) {
    say 'Ref of the key is: ' . ref($key);
    say 'Ref of the list elements produced by array-dereferencing the key are:';
    say ref($_) for @{ $key }; # no output, i.e. simple strings
    say 'List elements produced by array-dereferencing the key are:';
    say $_ for @{ $key }; # 'CODE(0x8d27f0)'
}

相反,使用Tie::RefHash。(代码评论:更喜欢这种带有->箭头的语法来取消引用 coderef。)

use 5.010;
use strict;
use warnings FATAL => 'all';
use Tie::RefHash qw();

my $coderef = sub {
    my ($name, $name2) = @_;
    say "hello $name and $name2";
};

$coderef->(qw(bob sue));

my %hash = (sayhi => $coderef);
$hash{sayhi}->(qw(bob sue));

tie my %hash2, 'Tie::RefHash';
%hash2 = ($coderef => 1);
foreach my $key (keys %hash2) {
    say 'Ref of the key is: ' . ref($key);   # 'CODE'
    $key->(qw(bob sue));
}
于 2012-05-29T09:02:35.957 回答
1

perlfaq4

如何使用引用作为哈希键?

(由 brian d foy 和 Ben Morrow 提供)

哈希键是字符串,因此您不能真正使用引用作为键。当您尝试这样做时,perl 会将引用转换为其字符串形式(例如, HASH(0xDEADBEEF) )。从那里你不能从字符串化的表单中取回引用,至少你自己没有做一些额外的工作。

请记住,即使引用的变量超出范围,散列中的条目仍然存在,并且 Perl 完全有可能随后在同一地址分配不同的变量。这意味着新变量可能会意外地与旧变量的值相关联。

如果您有 Perl 5.10 或更高版本,并且您只想针对引用存储一个值以供以后查找,您可以使用核心 Hash::Util::Fieldhash 模块。如果您使用多个线程(这会导致所有变量在新地址重新分配,更改它们的字符串化),这也将处理重命名键,并在引用的变量超出范围时垃圾收集条目。

如果您确实需要能够从每个哈希条目中获取真正的引用,您可以使用 Tie::RefHash 模块,它会为您完成所需的工作。

所以看起来Tie::RefHash会做你想做的事。但老实说,我不认为你想做的是一个特别好的主意。

于 2012-05-29T08:39:43.753 回答
0

你为什么需要它?如果您需要将参数存储到散列中的函数,您可以使用 HoH:

my %hash;
$hash{$subref} = { sub => $subref,
                   arg => [qw/bob sue/],
                 };
foreach my $key (keys %hash) {
    print "$key: ref type is: " . ref($key) . "\n";
    $hash{$key}{sub}->( @{ $hash{$key}{arg} } );
}

但是,无论如何,您可能仍然可以选择更好的密钥。

于 2012-05-29T08:38:47.460 回答