在eval中警告词汇变量"不可用"的原因是什么

ale*_*del 11 perl eval lexical-scope

eval语句在词法变量的范围内时,该变量应该在被评估块的词法上下文中.此外,词汇变量应该在subs的词汇上下文中可用.

然而,这不起作用:

use warnings;

{
    package VLE;

    my $ln10 = 2.302585092994045684017991454684;

    sub reval {
        say eval $_[0] if @_;
    }
}

package VLE;

reval( q($ln10) );
Run Code Online (Sandbox Code Playgroud)

它导致:

Variable "$ln10" is not available at (eval 1) line 1.
Run Code Online (Sandbox Code Playgroud)

但是,如果我(无用)在块中的任何位置使用词法变量,它在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) );
Run Code Online (Sandbox Code Playgroud)

版画

2.30258509299405
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

编辑:

引用的破坏不是问题,因为此代码(维护引用$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
Run Code Online (Sandbox Code Playgroud)

amo*_*mon 13

子例程不会关闭(捕获)所有可见的词法变量,而只会关闭子例程主体中引用的那些变量.这是参考计数正常工作的一个非常不可或缺的部分.捕获哪些变量是在编译时确定的,并且在执行子例程定义时捕获变量(命名subs的编译时间,匿名subs的运行时间).

这里你的第一个reval不捕获$ln10变量.因此,它在eval中不可用.因为eval字符串是运行时值,所以在确定应捕获哪些变量时不能将其考虑在内.

更确切地说,$ln10当保留封闭块时,变量被破坏,因为不存在对它的进一步引用.所以这段代码可以工作,因为在$ln10执行eval时变量仍然存在:

use warnings;
{
    package VLE;
    my $ln10 = 2.302585092994045684017991454684;

    sub reval {
        say eval $_[0] if @_;
    }

    reval(q($ln10));
    # $ln10 variable exists until here
}
Run Code Online (Sandbox Code Playgroud)

第二个eval确实捕获变量,因此它的生命周期延长到执行eval的时间点,并且一切都按预期工作.