【问题标题】:What is the reason for the warning that a lexical variable is "not available" within eval词法变量在 eval 中“不可用”的警告的原因是什么
【发布时间】:2018-03-17 07:50:41
【问题描述】:

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

【问题讨论】:

  • 我认为在普通括号中使用通用引号qqqqx 等是不好的做法。 q($ln10) 看起来太像一个子例程调用,并且比这里的 '$ln10' 好得多。即使你想引用一个包含单引号的字符串,像q/$str = 'aaa'/ 这样的东西会更可取,因为斜线是 Perl 常见的“替代引号”。我已经编辑了你的问题。
  • 我经常使用q(),因为我经常用perl -E '... say join("\n", split("", "foo ".q($foo))) ...'在终端中测试Perl的sn-ps,经常在' '里面有一个多行程序,包括上面两个,括号使它当单引号不可用时,易于跟踪块/引用级别,尤其是在终端中没有语法突出显示的情况下。有充分的理由去做所有你认为不好的事情。
  • 我什至更喜欢不带任何单引号的 sn-ps,因为我可以将它们直接粘贴到终端中,而无需使用 heredoc。
  • 我不明白你所说的 “所有 [我] 认为都是不好的做法”的意思。我在这里只是说我认为q(...) 是不好的做法,因为它很容易与子程序调用混淆。你在测试代码的 sn-ps 时经常做的事情在这里无关紧要,无论如何,如果你计算括号,我认为perl -E '... say join("\n", split("", "foo ". q/$foo/)) ...' 更简单。所有这些“重要原因”是什么?
  • “我什至更喜欢没有任何单引号的 sn-ps” 然后使用斜杠。但请不要模拟函数调用:即使空格在q( a b ) 中也很重要。我们仍在讨论我的意见

标签: perl eval lexical-scope


【解决方案1】:

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

这里您的第一个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 的时间点,并且一切都按预期工作。

【讨论】:

  • 块也很重要。 package 本身并不是一个词法作用域。这就是our 的用途。
  • 引用销毁在这里并不完全适用,因为维护引用仍然会导致它失败(请参阅我的编辑)。
  • @alexchandel 哦,这很有趣!我有预感为什么会出现这种情况(将词法变量映射到标量对象的“pad”表在离开范围时被破坏,因此即使值继续存在,内部子程序也无法找到变量值),但是在这个级别上,我们深入研究了 Perl 解释器的实现细节。最好将其视为未定义的行为,并且只期望您明确关闭的 eval 中的变量。
猜你喜欢
  • 1970-01-01
  • 2010-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-15
  • 1970-01-01
  • 2015-04-16
  • 1970-01-01
相关资源
最近更新 更多