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

zazmityj  于 2023-05-07  发布在  Perl
关注(0)|答案(4)|浏览(125)

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

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

$somvar =~ s/prefix1_FOO/prefix2_BAR/g;

当然,在这里,FOO解析为三个字母FOO,而不是扩展为常数。
我在网上查了一下,有人建议使用${\FOO}@{[FOO]},还有人提到了(?{FOO})。我想知道是否有人可以阐明哪些是正确的,以及是否有任何优势。或者,只使用非常数变量是否更好?(在我的情况下,性能是一个因素)。

rlcwz9us

rlcwz9us1#

没有太多的理由使用常量而不是变量。这并没有太大的区别-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
   }
);

提供:

Rate const   var
const 917095/s    --   -1%
var   923702/s    1%    --

真的没什么好担心的。
然而,值得注意的是,你可以用qr//编译正则表达式,这样做--假设RE是静态的--可能会提高性能(但可能不会,因为perl * 可以 * 检测静态正则表达式,并且它本身就能做到。

Rate      var    const compiled
var      910498/s       --      -2%      -9%
const    933097/s       2%       --      -7%
compiled 998502/s      10%       7%       --

代码如下:

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

不过说实话,这是一个微观优化。与您的代码相比,正则表达式引擎的速度很快,所以不必担心。如果性能对你的代码至关重要,那么处理它的正确方法是 * 首先 * 编写代码,然后 * 分析它以寻找要优化的热点。

v440hwme

v440hwme2#

所显示的问题是由于这些常量是裸字(在编译时构建)
使用此模块定义的常量不能像变量一样插入字符串。
在当前的实现(constant pragma)中,它们是“* 可内联子例程 *”(参见†)。
这个问题可以通过使用像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;

现在他们得到插值。请注意,更复杂的替换可能需要/e
这些是在运行时构建的,因此您可以将表达式的结果分配给它们。特别地,您可以使用qr运算符,例如

const my $patt => qr/$foo/i;  # case-insensitive

qr是构建正则表达式模式的推荐方法。(除非分隔符是',否则它会进行插值。)性能增益通常很小,但您可以得到一个正确的正则表达式,可以构建和使用它(在本例中也是一个常量)。
我推荐使用Const::Fast模块,而不是另一个模块,事实上,此时所有其他模块都是如此。请参阅recent article,其中详细讨论了两者。这是一个review的许多其他选项。
我强烈建议使用一个常量(你选择的类型)的东西是只读的。这对代码的健康和接触它的开发人员(包括你自己)都有好处。
†这些是子程序,我们需要能够 * 运行代码 *,以便用给定的值评估和替换它们。不能只是“插值”(计算)一个变量--它不是一个变量。
在字符串(需要插入,所以实际上是双引号)中运行代码的一种方法是解引用,其中在引用下的块中有一个表达式;则对表达式求值。因此,我们首先需要提到这一点。所以要么

say "@{ [FOO] }";  # make array reference, then dereference

say "${ \FOO }";   # make scalar reference then dereference

打印foo。请参阅文档了解为什么这样做和示例。因此,可以在正则表达式内部以及匹配和替换部分中执行相同的操作

s/prefix1_${\FOO}/prefix2_${\BAR}/g;

(or与@{[...]}),因为它们被评估为插值字符串。
哪个更好?这些都是把戏很少有,如果有的话,需要这样做。它有一个很好的机会混淆读者。所以我根本不建议诉诸这些手段。
至于(?{ code }),这是一个正则表达式特性,其中 * 代码在模式内部进行评估 *(仅匹配端)。它是复杂和棘手的,很少 * 需要 *。在perlretut和perlre中看到它。
讨论这些事情的速度并不重要。它们肯定不在干净和惯用代码的范围之内,而您很难检测到运行时的差异。
但如果你必须使用其中之一,我宁愿通过技巧在标量引用中插值,而不是使用复杂的正则表达式特性。

不是HASH引用

在极少数情况下,解决方案${\FOO}需要额外的调整,例如${\FOO}{6,20}
Perl抱怨道
不是HASH引用...
修复方法是添加额外的集群,如下所示:(?:${\FOO}){6,20}

gjmwrych

gjmwrych3#

根据PerlMonk的说法,如果你关心性能,你最好创建一个已经插值的字符串:

use constant PATTERN => 'def';
my $regex = qr/${\(PATTERN)}/; #options such as /m can go here.
if ($string =~ regex) { ... }

这里是whole discussion的链接。

avwztpqn

avwztpqn4#

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

my $var =~ s/prefix1_${\FOO}/prefix2_${\BAR}/g;

信用:https://www.perlmonks.org/?node_id=293323

相关问题