`/regex/o` 的真正含义是什么(曾经有过一次,但现在似乎消失了)?

U. *_*ndl 8 regex perl lazy-evaluation

(抱歉这个标题,但是这个“功能”确实让我困惑)

在学习 Perl 时,我了解到o使用变量的正则表达式的修饰符只会被评估一次,即使变量在初始评估后发生变化。最初看起来没有任何问题,这是明确指定的。

显然,在所使用的变量获得其值之前不能进行初始评估。

现在qr让生活变得更有趣了。考虑以下代码(也在定义其他变量的循环中执行):

{
    my $n = $name;
    $n =~ s/[^\w\.-]/_/g;
    $n = qr:^${n}\@${another_variable}$:o;
    @a = grep { !/$n/ } @a;
}
Run Code Online (Sandbox Code Playgroud)

当直接使用正则表达式时qr,人们可能会争辩说正则表达式只编译一次,即使变量的范围超出范围(超出范围是否被视为变量的更改?)

但是当使用qr构建正则表达式并将其分配给词法变量时,编译的正则表达式将超出范围,因此我期望正则表达式不能重用并且将被重新构建(基本思想是内部的正则表达式grep应该不必为每次迭代而重建)。

由于生活是残酷的,似乎引用的整个正则表达式$n从未被重建,因此使用第一个值直到程序停止。

有趣的是,在 Perl 5.18.2(正在使用的版本)中不再提及修饰符operlre(1)而 Perl 5.26.1 在相应的页面中说:

o - 假装优化你的代码,但实际上引入了错误

那么任何人都可以解释“一次”评估的规则(以及语义在 Perl 的生命周期中是否发生了变化)?

有关的:

amo*_*mon 6

Perl 有几个结构,它们不仅将状态存储在变量中,还存储操作码本身的一些状态。除了正则/o表达式模式之外,这还包括..触发器运算符(在标量上下文中)或state变量。

也许state变量是最清楚的,因为它对应于static许多其他语言(例如C)中的局部变量。变量state在程序的生命周期内最多初始化一次。表达式state $var = initialize()可以理解为

my $var;
if (previously_initialized) {
   $var = cached_value;
} else {
   $var = initialize();
}
Run Code Online (Sandbox Code Playgroud)

这不会跟踪initialize()表达式中的依赖关系,而仅对其求值一次。

/.../o同样,将正则表达式模式视为一种隐藏状态变量也是有意义的state $compiled_pattern = qr/.../

/o很久以前,当正则表达式被即时编译时,该功能是一个好主意,类似于它在其他语言中的工作方式,其中正则表达式模式作为字符串提供给搜索函数。

长期以来,出于性能目的,它并不是必需的,并且仅在进行变量插值时才有效。但如果您确实想要这种行为,那么使用state变量可以更清楚地传达该意图。因此,我认为修饰符没有适当的用途/o


zdi*_*dim 6

很大程度上已过时/o”( perlop) 标志仍然具有“曾经”的含义和操作。虽然它几乎没有被提及,但它在 perlop 中perlre得到了解决

/PATTERN/msixpodualngc
...
... Perl 不会重新编译该模式,除非它包含的插值变量发生变化。您可以通过在尾部分隔符后添加/o(代表“一次”) 来强制 Perl 跳过测试并且从不重新编译。曾几何时,Perl 会不必要地重新编译正则表达式,为了提高速度,这个修饰符可以告诉它不要这样做。但现在,使用的唯一原因/o之一是:

  1. 这些变量有数千个字符长,并且您知道它们不会改变,并且您需要通过让 Perl 跳过对此的测试来提高最后一点速度。(这样做会带来维护上的损失,因为提及 /o 就意味着您不会更改模式中的变量。如果您确实更改了它们,Perl 甚至不会注意到。)
  2. 您希望模式使用变量的初始值,无论它们是否更改。(但是有比使用 /o 更明智的方法来实现这一点。)
  3. 如果模式包含嵌入代码,例如
use re 'eval';  
$code = 'foo(?{ $x })';  
/$code/  
Run Code Online (Sandbox Code Playgroud)

那么 perl 每次都会重新编译,即使模式字符串没有改变,以确保$x每次都能看到当前的值。/o如果您想避免这种情况,请使用。

最重要的是,使用 /o 几乎从来都不是一个好主意。

因此,事实上,显然它甚至不会测试要插值的变量是否发生变化,这可能有合法的用途。但事实上,总而言之,它可能不应该被使用。

演示“一次”操作的示例

perl -Mstrict -wE'
    sub tt { 
        my ($str, $v) = @_; 
        my $re = qr/$v/o; 
        $str =~ s/$re/X/; 
        return $str 
    };  
    say tt( q(a1), $_ ) for qw(a b c)'
Run Code Online (Sandbox Code Playgroud)

对于-ed 模式或正则表达式/o上的,每次都会匹配(更改)该字符串,即使仅在第一次迭代中为模式传递。显然,该模式不会重新编译,因为变量稍后具有和 then且不应该匹配。qra1abc

只有/o第一次迭代才有正则表达式匹配。

在每次迭代中,词法$re显然会随着整个函数的消失而消失,但其最初编译的值仍然会被使用。这意味着某些操作/o是“非常”全球化的。这种顽固的全球行为在其他一些古老的特征中并非闻所未闻(glob例如,我想到的)。

如果 sub 是词法变量中的代码引用,并且始终在动态作用域中重新创建,则情况也是如此。

perl -Mstrict -wE'
    for my $c (qw(a b c)) { 
        my $tt = sub { 
            my ($str, $v) = @_; 
            my $re = qr/$v/o; 
            $str =~ s/$re/X/; 
            return $str 
        }; 
        say $tt->( q(a1), $c ) 
    }'
Run Code Online (Sandbox Code Playgroud)

全部打印X1三遍。