【问题标题】:How can I detect if a lexical variable exists (is declared) in a package, even if uninitialized?即使未初始化,如何检测包中是否存在(已声明)词法变量?
【发布时间】:2020-05-09 17:20:04
【问题描述】:

假设我有东西

package Foo;
my $bar;

我如何判断 $bar 是否已声明而不管它是否未初始化?

在某种程度上我认为这是可能的,因为warnings 知道它,这里warnings 知道变量$bar 存在,所以你只会收到一个未初始化的警告。

$ perl -wE'my $bar; print $bar;'
Use of uninitialized value $bar in print at -e line 1.

但是once 警告类知道这里甚至没有声明变量$baz

$ perl -wE'my $bar; print $baz;'
Name "main::baz" used only once: possible typo at -e line 1.
Use of uninitialized value $baz in print at -e line 1.

同样,strict 将停止编译,因此它也必须知道这一点(注意 die 永远不会触发)。

$ perl -wE'use strict; my $bar; print $baz; die 42;'
Global symbol "$baz" requires explicit package name (did you forget to declare "my $baz"?) at -e line 1.
Execution of -e aborted due to compilation errors.

所以它也必须知道这一点。

【问题讨论】:

  • 只是想一想,我们先忽略包和词法共享同一个变量名的可能性,引用变量,检查它是否存在于当前包哈希表中。
  • @Сухой27 包哈希表没有词法变量。它只有包变量(our
  • 这就是重点,如果你在那里没有找到任何东西,那么你就参考了一个词汇。
  • 您是否尝试过使用PadWalker 模块?在 Perl 中没有内置的方法可以对词法变量进行反射,因此您需要一些 XS 代码。
  • 听起来您正在尝试确定是否已声明 lexical 变量,这是严格的“vars”要求的事情之一。要以编程方式检测这一点,您需要使用 PadWalker 之类的东西查看垫。 pad 与任何包存储无关,仅与词法范围无关。

标签: perl variables variable-declaration


【解决方案1】:

PadWalker 可以检查已定义的词法变量。

use PadWalker ':all';
my $foo;

my $level = -1;
while (my $pad = eval { peek_my(++$level) }) {
    print "$_ is declared in scope $level\n" for keys %$pad;
}

$level is defined in scope 0
$foo is defined in scope 0

【讨论】:

  • 解释:Perl 不在作用域出口或子出口释放词法变量。它们继续存在于 sub 的 scratchpad aka pad 中。 (事实上​​,对于 sub 达到的每个递归级别都有一个 pad,因此可能存在 var 的多个副本,即使 sub 实际上没有运行!)虽然变量是通过索引查找到 pad , var 的名称出现在那里(在一些错误/警告消息中使用)。 peek_mypeek_our 导航潜艇垫。 (请记住,our 变量也是词法变量!)
  • 请注意,一个 sub 可以访问多个具有相同名称的 var,这些 var 可能是词法和非词法的混合。因此,这可能会失败,具体取决于使用地点/时间。
【解决方案2】:
package Foo;
my $x;
my $eval_fmt = 'use strict; %s || 1';
eval( sprintf $eval_fmt, '$y' ) or warn 'No such variable: $y';
eval( sprintf $eval_fmt, '$x' ) and warn 'Variable $x exists';

【讨论】:

  • 声明的包变量 (use vars qw( $x );) 也就是导入的包变量 (use Config qw( %Config );) 会失败
【解决方案3】:

无需 Padwalker(打印一次“is lex”):

package Foo;

print "is lex\n" if eval('\\$' . __PACKAGE__ . '::foo') != \$foo;
my $foo;
print "is lex\n" if eval('\\$' . __PACKAGE__ . '::foo') != \$foo;

【讨论】:

  • 对于别名为同名包变量的词法失败。 (Ex1:our $foo Ex2:use experimental qw( refaliasing ); \my $foo = \$foo;)如果包 var 是别名的包,也会失败。 (*foo = \my $foo;) 这有关系吗?我不知道。我不知道为什么代码会关心 var 是否是词法。
【解决方案4】:

需要明确的是,词法变量不是任何包的一部分,根本不与包系统交互;这是一个完全不同的会计。无论任何包声明,它都属于它所在的范围。该范围可以是封闭的大括号、声明它的子例程或文件。

您展示的大多数示例(oncestrict)都处理包变量。为此,您只需查看 stash 以查看名称是否已定义。

对于未初始化的情况,perl 在尝试使用变量时发现变量没有值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-12
    • 1970-01-01
    • 2016-10-03
    • 2022-12-03
    • 2015-10-24
    • 2022-01-08
    相关资源
    最近更新 更多