如何使用'子程序引用'作为哈希键

时间:2012-05-29 08:13:37

标签: perl hash reference subroutine dereference

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

在以下示例代码中,

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

    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.
    

3 个答案:

答案 0 :(得分:2)

这是正确的,普通的哈希键只是一个字符串。非字符串的东西会被强制转换为字符串表示。

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

Tie是修改该行为的常用方法,但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));
}

答案 1 :(得分:1)

来自perlfaq4

  

How can I use a reference as a hash key?

     

(由brian d foy和Ben Morrow提供)

     

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

     

请记住,哈希中的条目仍然存在,即使是   引用变量超出范围,完全是   可能Perl随后在其中分配一个不同的变量   同一地址。这将意味着一个新变量可能意外   与旧的价值相关联。

     

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

     

如果你真的需要能够从每个人那里获得真实的参考资料   哈希条目,你可以使用Tie :: RefHash模块,它做的   需要为你工作。

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

答案 2 :(得分: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} } );
}

但是,无论如何,你可以选择更好的钥匙。

相关问题