Perl 如何使用';子程序引用';作为散列键

Perl 如何使用';子程序引用';作为散列键,perl,hash,reference,subroutine,dereference,Perl,Hash,Reference,Subroutine,Dereference,在Perl中,我正在学习如何去引用“子例程引用”。但我似乎不能将子例程引用用作哈希“键” 在下面的示例代码中 我可以创建对子例程(&$subref)的引用,然后取消引用以运行子例程(&$subref) 我可以将引用用作散列“值”,然后轻松地取消引用该值 但我不知道如何将引用用作哈希“键”。当我从散列中取出密钥时,Perl将密钥解释为一个字符串值(而不是一个引用)——我现在正在这样做(多亏了这个站点!)。所以我尝试了Hash::MultiKey,但这似乎将其转换为数组引用。我想将其视为一个子例程/

在Perl中,我正在学习如何去引用“子例程引用”。但我似乎不能将子例程引用用作哈希“键”

在下面的示例代码中

  • 我可以创建对子例程(&$subref)的引用,然后取消引用以运行子例程(&$subref)
  • 我可以将引用用作散列“值”,然后轻松地取消引用该值
  • 但我不知道如何将引用用作哈希“键”。当我从散列中取出密钥时,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.
    

    你为什么需要它?例如,如果需要将参数存储到散列中的函数,可以使用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} } );
    }
    
    但是,无论如何,您都可以选择一个更好的键。

    来自:

    (布莱恩·福伊和本·莫罗撰稿)

    散列键是字符串,因此不能真正使用引用作为键。 当您尝试这样做时,perl会将引用转换为字符串化引用 表单(例如,哈希(0xDEADBEEF))。从那里你再也回不来了 引用来自字符串化的表单,至少不做任何操作 自己做额外的工作

    请记住,即使 引用的变量超出范围,并且它完全是 Perl随后可以在 同一地址。这意味着一个新变量可能会意外出现 与旧数据库的值关联

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

    如果你真的需要从每个人那里得到一个真实的参考 hash条目,您可以使用Tie::RefHash模块,该模块执行以下操作: 你需要的工作


    看来你想干什么就干什么。但是说实话,我不认为你想做的是一个特别好的主意。

    没错,一个普通的散列键只是一个字符串。非字符串的事物会被强制为其字符串表示形式

    my $coderef = sub { my ($name, $name2) = @_; say "hello $name and $name2"; };
    my %hash2 = ( $coderef => 1, );
    print keys %hash2;  # 'CODE(0x8d2280)'
    
    ing是修改该行为的常用方法,但对您没有帮助,它有一个不同的用途:顾名思义,您可能有多个键,但同样只有简单的字符串:

    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)'
    }
    
    相反,使用。(代码评论:更喜欢使用带有->箭头的语法来取消对coderef的引用。)


    谢谢,戴夫。危险在于我可能会无意中删除变量(例如,如果引用的变量超出范围)?为了帮助我理解,您是否能够通过扩展daxim的代码示例来说明这种潜在的危险?也许通过添加“危险范围”场景?例如,我相信qw(bob sue)可能会被qw(fred barney)打败?也许我误解了。非常感谢您的指导!我说“我不认为你想做的是一个特别好的主意”,不是因为它很危险,而是因为几乎可以肯定有更好的方法来做你想做的事情。试图使用散列键作为引用是很奇怪的。这不是他们的目的。散列键只是标识散列值的字符串。这就像说你希望数组索引是实数一样。有很多方法可以满足我的需要。我只是觉得这是一条有趣的路线:“过程作为索引”。虽然很奇怪,但我觉得它很有趣。再次感谢这个社区对您的周到回复!但是,首先为什么要尝试使用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));
    }