perl在正则表达式中使用常量

use*_*918 2 regex perl

我想知道在perl正则表达式中使用常量.我想做类似的事情:

use constant FOO => "foo"
use constant BAR => "bar"

$somvar =~ s/prefix1_FOO/prefix2_BAR/g;
Run Code Online (Sandbox Code Playgroud)

当然,在那里,FOO解决了三个字母F O O而不是扩展到常数.

我在线查看,有人建议使用其中任何一个${\FOO}@{[FOO]}其他人提到的(?{FOO}).我想知道是否有人可以了解哪些是正确的,以及是否有任何优势.或者,使用非常量变量更好吗?(性能是我的一个因素).

Sob*_*que 7

在变量上使用常量的原因并不多.它没有太大的区别 - perl无论如何都会编译一个正则表达式.

例如:

#!/usr/bin/perl

use warnings;
use strict;
use Benchmark qw(:all);

use constant FOO => "foo";
use constant BAR => "bar";

my $FOO_VAR = 'foo';
my $BAR_VAR = 'bar';

sub pattern_replace_const {
   my $somvar = "prefix1_foo test";
   $somvar =~ s/prefix1_${\FOO}/prefix2_${\BAR}/g;
}

sub pattern_replace_var {
   my $somvar = "prefix1_foo test";
   $somvar =~ s/prefix1_$FOO_VAR/prefix2_$BAR_VAR/g;
}

cmpthese(
   1_000_000,
   {  'const' => \&pattern_replace_const,
      'var'   => \&pattern_replace_var
   }
);
Run Code Online (Sandbox Code Playgroud)

得到:

          Rate const   var
const 917095/s    --   -1%
var   923702/s    1%    --
Run Code Online (Sandbox Code Playgroud)

真的不够担心.

然而,值得注意的是 - 您可以编译正则表达式并以qr//这种方式执行,如果RE是静态的 - 可能会提高性能(但它可能不会,因为perl 可以检测静态正则表达式,并且自己这样做.

    Rate      var    const compiled
var      910498/s       --      -2%      -9%
const    933097/s       2%       --      -7%
compiled 998502/s      10%       7%       --
Run Code Online (Sandbox Code Playgroud)

使用以下代码:

my $compiled_regex = qr/prefix1_$FOO_VAR/;
sub compiled_regex { 
    my $somvar = "prefix1_foo test";
    $somvar =~ s/$compiled_regex/prefix2_$BAR_VAR/g;
}
Run Code Online (Sandbox Code Playgroud)

老实说 - 这是微观优化.与您的代码相比,正则表达式引擎很快,所以不要担心它.如果性能对您的代码至关重要,那么处理它的正确方法是首先编写代码,然后对其进行分析以寻找要优化的热点.

  • 我认为常量的关键不是它们可能比变量更快,而是它们是*常数*. (4认同)

zdi*_*dim 7

显示的问题是由于这些常量是裸词(在编译时构建)

使用此模块定义的常量不能像变量一样插入到字符串中。

当前的实现constantpragma)中,它们是“可内联的子程序”(参见)。

使用像Const::Fast这样的模块可以很好地解决这个问题

use Const::Fast;

const my $foo => 'FOO';
const my $bar => 'BAR';

my $var = 'prefix1_FOO_more';

$var =~ s/prefix1_$foo/prefix2_$bar/g;
Run Code Online (Sandbox Code Playgroud)

现在他们确实得到了插值。请注意,可能需要更复杂的替换/e

这些是在运行时构建的,因此您可以将表达式的结果分配给它们。特别是,您可以使用qr 运算符,例如

const my $patt => qr/$foo/i;  # case-insensitive 
Run Code Online (Sandbox Code Playgroud)

qr是构建正则表达式模式的推荐方法。(除非分隔符是 ,否则它会进行插值'。)性能增益通常很小,但您会得到一个合适的正则表达式,可以这样构建和使用(在这种情况下也是一个常量)。

Const::Fast很容易推荐模块而不是另一个,事实上在这个时候超过所有其他模块。请参阅最近的一篇文章,其中对两者进行了详细讨论。这是对许多其他选项的回顾

我强烈建议对只读的事物使用常量(您选择的类型)。这对代码的健康以及接触它的开发人员(包括众所周知的六个月内的您自己)的健康都有好处。


这些是子例程,我们需要能够运行代码以便评估它们并用给定值替换它们。不能只是“插入”(评估)一个变量——它不是一个变量。

在字符串中运行代码的一种方法(需要插值,因此有效地双引号)是取消引用,其中引用下的块中有一个表达式;然后计算表达式。所以我们需要先做那个参考。所以要么

say "@{ [FOO] }";  # make array reference, then dereference
Run Code Online (Sandbox Code Playgroud)

或者

say "${ \FOO }";   # make scalar reference then dereference
Run Code Online (Sandbox Code Playgroud)

打印foo。请参阅文档以了解其工作原理和示例。因此,您可以在正则表达式中执行相同的操作,并且可以在匹配和替换部分中执行相同的操作

s/prefix1_${\FOO}/prefix2_${\BAR}/g;
Run Code Online (Sandbox Code Playgroud)

(或与@{[...]}),因为它们被评估为内插字符串。

哪个更好”?这些都是技巧。很少(如果有的话)要这样做。它有很好的机会迷惑读者。所以我根本不建议诉诸这些。

至于(?{ code }),这是一个正则表达式功能,其中代码在模式内进行评估(仅匹配侧)。它既复杂又棘手,而且很少需要在 perlretutperlre 中查看它。

讨论这些事情的速度并不重要。它们肯定超出了干净和惯用代码的范围,而您很难检测到运行时差异。

但是,如果您必须使用其中之一,我宁愿通过技巧在标量引用中进行插值,然后再使用复杂的正则表达式功能。