4
$ref = \%hash;
$ref = \@hash;

如何在 perl中使用typeglob做与引用相同的事情?

perl 解释的确切步骤是$$ref{key}什么?

4

4 回答 4

10

使用perlref 文档的*foo{THING}Make References 部分中记录的语法。

可以使用一种特殊的语法来创建引用,这种语法被人们亲切地称为*foo{THING}语法。*foo{THING}返回对 THING 槽的引用*foo(这是符号表条目,其中包含所有称为 的内容foo)。

$scalarref = *foo{SCALAR};
$arrayref  = *ARGV{ARRAY};
$hashref   = *ENV{HASH};
$coderef   = *handler{CODE};
$ioref     = *STDIN{IO};
$globref   = *foo{GLOB};
$formatref = *foo{FORMAT};

例如:

#! /usr/bin/env perl

use strict;
use warnings;

our %hash = (Ralph => "Kramden", Ed => "Norton");
our @hash = qw/ apple orange banana cherry kiwi /;

my $ref;

$ref = *hash{HASH};
print $ref->{Ed}, "\n";

$ref = *hash{ARRAY};
print $ref->[1], "\n";

输出:

诺顿
橙

至于你问题的第二部分,添加

print $$ref{Ralph}, "\n";

在 Ed 产生预期的输出之后。编译器为此行生成代码,该代码经过以下顺序:

  1. 获取$ref.
  2. 得到$ref的东西。
  3. 在步骤 2 的哈希中查找密钥。

但不要相信我的话。为了减少输出量,考虑一个类似的两线:

my $ref = { Ralph => "Kramden" };
print $$ref{Ralph};

使用为调试编译的 perl 运行它让我们

$ debugperl -Dtls 参考
[...]
(参考:1)下一个状态
    =>  
(参考:2)pushmark
    => *  
(ref:2) padsv($ref) # 第 1 步
    => * \HV()  
(ref:2) rv2hv # 第 2 步
    => * 高压()  
(ref:2) const(PV("Ralph"\0)) # STEP 3a
    => * HV() PV("拉尔夫"\0)  
(ref:2) helem # STEP 3b
    => * PV("克拉姆登"\0)  
(参考:2)打印
    => SV_YES  
(ref:2) 离开
[...]

请注意,对于全局变量,它略有不同。

我不确定您的更大意图是什么,但有一些重要的警告。请注意,typeglob 表示符号表条目,因此您无法以这种方式获取词法,因为它们存在于 pad 中,而不是符号表中。例如,假设您在上面代码中my @hash = ("splat");的赋值之前插入$ref。结果可能会让你大吃一惊。

$ ./编
“我的”变量 @hash 掩盖了 ./prog 第 11 行相同范围内的早期声明。
诺顿
橙

关于标量的行为也可能令人惊讶。

*foo{THING}undef如果该特定 THING 尚未使用,则返回,标量除外。如果尚未使用,*foo{SCALAR}则返回对匿名标量的引用。$foo这可能会在未来的版本中改变。

告诉我们您想要做什么,我们将能够为您提供具体、有用的建议。

于 2011-06-28T13:10:52.827 回答
8

如果您要问如何获得对类型 glob 的引用,那就是:

my $ref = \*symbol_name_here;

对于符号的“字面名称”(即您输入符号的确切名称的位置),而不是变量。但是,您可以这样做:

my $ref = Symbol::qualify_to_ref( $symbol_name );

对于变量符号。但是,以上适用,strict而以下更简单的则不适用:

my $ref = \*{$symbol_name};

的好处之一Symbol::qualify*是它将包名称作为第二个变量处理。所以...

my $sref = Symbol::qualify_to_ref( $symbol_name, $some_other_package );

做同样的事情\*{$some_other_package.'::'.$symbol_name},它适用于strict.

一旦你有了符号 ref,为了得到这个槽,你必须尊重这个引用,所以 perl 不认为你正在尝试将它用作哈希,就像这样。

my $href  = *{ $sref }{HASH};
my $code  = *{ $sref }{CODE};
my $arref = *{ $sref }{ARRAY};
my $io    = *{ $sref }{IO};

另一个镜头

我以不同的方式把你的两个想法放在一起。如果你有一个符号表引用,你可以得到这个HASH槽,这是一个引用,就像对哈希的任何其他引用一样。因此,您可以执行以下操作。

任何一个

*hash{HASH}->{key}

或者

${ *hash{HASH} }{key}

将工作。不过这些更安全

( *hash{HASH} || {} )->{key};
${ *hash{HASH} || {} }{key};

如果您不想使用直接条目,而是使用对表的引用来执行此操作,您可以执行以下操作:

my $ref   = \*hash;
my $value = *{ $ref }{HASH} && *{ $ref }{HASH}->{key};

注意: %hash绝对需要是一个包变量。只有包变量驻留在符号表中(因此只有 subs@ISA和 Exporter 变量往往在现代符号表中)。词法变量(那些声明my的)驻留在“pad”中。


更新:

  • 我已经摆脱了使用Symbol这么多。奇怪的是,即使它是core,它在 Perlers 所做的和看到的事情上似乎是非标准的。相反,我在我所谓的“无块”中使用直接方式,尽可能本地化。

    # All I want is one symbol. 
    # Turn strict-refs off, get it, turn it back on.
    my $symb = do { no strict 'refs'; \*{$symbol_name} };
    

    或者

    {   no strict 'refs';
        *{$package_name.'::'.$symbol_name} = $sub_I_created;
        ... # more reckless symbol table mucking...
    }
    
  • 我几乎总是使用*STDERR{IO}成语作为 glob 文件句柄引用。在现代 Perl 中,这些通常是对象

    my $fh = *STDERR{IO};
    say blessed( $fh ); # IO::File
    $fh->say( 'Some stuff' );
    
于 2011-06-28T13:12:38.817 回答
1

我不认为你可以用 typeglobs 做与引用一样的事情(例如,词法变量从不涉及类型 glob)。你最终想要达到什么目的?

至于$$ref{key}, perl 进行如下:

  • 第一个 $ 告诉它返回一个标量
  • 在 sigil 之后存在变量表明该变量必须是引用
  • 表示{...}必须$ref是哈希引用
于 2011-06-28T11:51:35.907 回答
1

Perl 以实验性着称:

尝试:

 perl -MO=Terse -e' $ref = \%hash; $ret = $$ref{key}'

或者:

 perl -MO=Debug -e' $ref = \%hash; $ret = $$ref{key}'

一切都会被揭示。如果你有一个调试 Perl,你甚至可以这样做:

 perl -Dt -e'$ref = \%hash; $ret = $$ref{key}'
于 2011-06-28T11:51:50.653 回答