eval 中出现词法变量 "not available" 警告的原因是什么
What is the reason for the warning that a lexical variable is "not available" within eval
当 eval
语句在词法变量的范围内时,该变量应该在计算块的词法上下文中。此外,词法变量应该在 subs 的词法上下文中可用。
但这不起作用:
use warnings;
{
package VLE;
my $ln10 = 2.302585092994045684017991454684;
sub reval {
say eval $_[0] if @_;
}
}
package VLE;
reval( q($ln10) );
结果是:
Variable "$ln10" is not available at (eval 1) line 1.
但是如果我(无用地)在块中的任何地方使用词法变量,它就会突然在 eval 中可用:
use warnings;
{
package VLE;
my $ln10 = 2.302585092994045684017991454684;
sub reval {
say eval $_[0] if @_;
my (undef) = $ln10;
return 0
}
}
package VLE;
reval( q($ln10) );
打印
2.30258509299405
为什么会这样?
编辑:
引用的破坏不是问题,因为此代码(维护对 $ln10
的引用)也失败了:
use warnings;
{
package VLE;
my $ln10 = 2.302585092994045684017991454684;
sub reval2 {
say eval $_[0] if @_;
my (undef) = $ln10;
return 0
}
sub reval {
say eval $_[0] if @_;
return 0
}
}
package VLE;
reval( q($ln10) ); # still fails
reval2( q($ln10) ); # works
子例程不会关闭(捕获)所有可见的词法变量,而只会关闭那些在子例程主体中引用的词法变量。这是引用计数正常工作不可或缺的一部分。捕获哪些变量是在编译时确定的,并且在执行子例程定义时捕获变量(命名子程序的编译时间,匿名子程序的运行时间)。
这里您的第一个 reval
没有捕获 $ln10
变量。因此,它在 eval 内部不可用。因为 eval 字符串是一个 运行 时间值,所以在确定应该捕获哪些变量时不能考虑它。
更准确地说,$ln10
变量在封闭块离开时被销毁,因为不存在对它的进一步引用。所以这段代码可以工作,因为在执行 eval 时 $ln10
变量仍然存在:
use warnings;
{
package VLE;
my $ln10 = 2.302585092994045684017991454684;
sub reval {
say eval $_[0] if @_;
}
reval(q($ln10));
# $ln10 variable exists until here
}
第二个 eval
确实捕获了变量,因此它的生命周期被延长到执行 eval 的时间点,并且一切都按预期工作。
当 eval
语句在词法变量的范围内时,该变量应该在计算块的词法上下文中。此外,词法变量应该在 subs 的词法上下文中可用。
但这不起作用:
use warnings;
{
package VLE;
my $ln10 = 2.302585092994045684017991454684;
sub reval {
say eval $_[0] if @_;
}
}
package VLE;
reval( q($ln10) );
结果是:
Variable "$ln10" is not available at (eval 1) line 1.
但是如果我(无用地)在块中的任何地方使用词法变量,它就会突然在 eval 中可用:
use warnings;
{
package VLE;
my $ln10 = 2.302585092994045684017991454684;
sub reval {
say eval $_[0] if @_;
my (undef) = $ln10;
return 0
}
}
package VLE;
reval( q($ln10) );
打印
2.30258509299405
为什么会这样?
编辑:
引用的破坏不是问题,因为此代码(维护对 $ln10
的引用)也失败了:
use warnings;
{
package VLE;
my $ln10 = 2.302585092994045684017991454684;
sub reval2 {
say eval $_[0] if @_;
my (undef) = $ln10;
return 0
}
sub reval {
say eval $_[0] if @_;
return 0
}
}
package VLE;
reval( q($ln10) ); # still fails
reval2( q($ln10) ); # works
子例程不会关闭(捕获)所有可见的词法变量,而只会关闭那些在子例程主体中引用的词法变量。这是引用计数正常工作不可或缺的一部分。捕获哪些变量是在编译时确定的,并且在执行子例程定义时捕获变量(命名子程序的编译时间,匿名子程序的运行时间)。
这里您的第一个 reval
没有捕获 $ln10
变量。因此,它在 eval 内部不可用。因为 eval 字符串是一个 运行 时间值,所以在确定应该捕获哪些变量时不能考虑它。
更准确地说,$ln10
变量在封闭块离开时被销毁,因为不存在对它的进一步引用。所以这段代码可以工作,因为在执行 eval 时 $ln10
变量仍然存在:
use warnings;
{
package VLE;
my $ln10 = 2.302585092994045684017991454684;
sub reval {
say eval $_[0] if @_;
}
reval(q($ln10));
# $ln10 variable exists until here
}
第二个 eval
确实捕获了变量,因此它的生命周期被延长到执行 eval 的时间点,并且一切都按预期工作。