我正在尝试在正则表达式中重载常量.这是我的Tagger包:
package Tagger;
use overload;
sub import { overload::constant 'qr' => \&convert }
sub convert {
my $re = shift;
$re =~ s/\\nom/((?:[A-Z]{1}[a-z]+\\s*){2,3}(\\((\\w|\\s)+\\)+?)*)/xg;
return $re;
}
1;
Run Code Online (Sandbox Code Playgroud)
这是我想要触发重载的子例程:
sub ChopPattern {
my $string= shift;
my $pattern = shift;
if($string =~ m/$pattern/) {
$string =~ s/$&/ /g;
return ($string, $&);
} else {
return ($string, '');
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的测试:
$test = "foo bar Max Fast bar foo";
($test, $name) = ChopPattern($test, '\nom');
say $test;
say $name;
Run Code Online (Sandbox Code Playgroud)
如果我硬连线测试模式\nom
,则在子程序的匹配中:
sub ChopPattern {
my $string= shift;
my $pattern = shift;
if($string =~ m/\nom/) {
$string =~ s/$&/ /g;
return ($string, $&);
} else {
return ($string, '');
}
}
Run Code Online (Sandbox Code Playgroud)
测试得出正确的答案:
foo bar bar foo
Max Fast
Run Code Online (Sandbox Code Playgroud)
但如果我$pattern
在上面的比赛中使用测试产量:
foo bar Max Fast bar foo
<null line>
Run Code Online (Sandbox Code Playgroud)
有没有理由\nom
触发Tagger,但变量等于\nom
不?
以下是正在使用的Perl版本的详细信息:
This is perl 5, version 16, subversion 3 (v5.16.3) built for MSWin32-x64-multi-thread (with 1 registered patch, see perl -V for more detail)
Copyright 1987-2012, Larry Wall
Binary build 1604 [298023] provided by ActiveState http://www.ActiveState.com
Built Apr 14 2014 15:29:45
Run Code Online (Sandbox Code Playgroud)
有没有理由
\nom
触发Tagger,但变量等于\nom
不?
因为'\nom'
是字符串文字,而不是正则表达式的常量:
$ perl -Moverload -E'BEGIN { overload::constant qr => sub { say "@_" } } $foo =~ "bar"'
$ perl -Moverload -E'BEGIN { overload::constant qr => sub { say "@_" } } $foo =~ /bar/'
bar bar qq
Run Code Online (Sandbox Code Playgroud)
你正在做的是一个坏主意.以下实现更容易理解,并且不会在任何地方更改正则表达式语义:
use strict;
use warnings 'all';
use 5.010;
sub chop_pattern {
my ($string, $pattern) = @_;
my %mapping = (
'\nom' => qr/((?:[A-Z][a-z]+\s*){2,3}(?:\([\w\s]+\)+?)*)/
);
if (exists $mapping{$pattern}) {
my $matched = $string =~ s/$mapping{$pattern}/ /g;
return $string, $1 if $matched;
}
return $string, '';
}
my ($string, $chopped) = chop_pattern('foo Bar Baz qux', '\nom');
say "<$string> <$chopped>";
Run Code Online (Sandbox Code Playgroud)
输出:
<foo qux> <Bar Baz >
Run Code Online (Sandbox Code Playgroud)
我猜你是因为你想要处理多个"魔法"字符串(例如\nom
).我用一个简单的哈希将字符串映射到正则表达式.
《Programming Perl》说这overload::constant
适用于常量。
每当 Perl 记号器遇到常量时,您为整数和浮点数提供的任何处理程序都会被调用。
当您调用 时m/$pattern/
,这不是一个常量。这是一个变量。
Run Code Online (Sandbox Code Playgroud)($test, $name) = ChopPattern($test, '\nom');
现在,这'\nom'
是一个常量,但它是一个字符串。将其转换为 a qr//
,您将得到一个包含常量的正则表达式。
($test, my $name) = ChopPattern($test, qr'\nom');
Run Code Online (Sandbox Code Playgroud)
中的模式匹配ChopPattern
可以保持不变:
if($string =~ m/$pattern/) { ... }
Run Code Online (Sandbox Code Playgroud)
因为现在正则表达式中有一个常量部分,所以 Perl 可以调用您的convert
重载,并执行您的正则表达式。
让我们看看实际情况。请记住,Perl 在编译时解析源代码时执行此重载替换。
考虑这个例子:
BEGIN {
overload::constant 'qr' => sub {
my $re = shift;
$re =~ s/\\nom/foobar/;
return $re;
};
}
sub match {
my ( $t, $p ) = @_;
$t =~ m/$p/;
}
match( 'some text', '\nom' );
Run Code Online (Sandbox Code Playgroud)
代码的作用并不重要。当我们解析它时,我们得到以下输出:
$ perl -MO=Deparse scratch.pl
sub BEGIN {
use warnings;
use strict;
use feature 'say';
overload::constant('qr', sub {
my $re = shift();
$re =~ s/\\nom/foobar/;
return $re;
}
);
}
sub match {
use warnings;
use strict;
use feature 'say';
BEGIN {
$^H{'qr'} = 'CODE(0x147a048)';
}
my($t, $p) = @_;
$t =~ /$p/;
}
use warnings;
use strict;
use feature 'say';
BEGIN {
$^H{'qr'} = 'CODE(0x147a048)';
}
match 'some text', '\\nom'; # <-- here
Run Code Online (Sandbox Code Playgroud)
我们可以看到处理程序已安装,但在函数调用的最后一行,有字符串'\\nom'
。
现在,如果我们使用带引号的表达式qr//
而不是字符串,情况就会发生变化。
BEGIN {
overload::constant 'qr' => sub {
my $re = shift;
$re =~ s/\\nom/foobar/;
return $re;
};
}
sub match {
my ( $t, $p ) = @_;
$t =~ m/$p/;
}
match( 'some text', qr/\nom/ );
Run Code Online (Sandbox Code Playgroud)
现在,解析后的程序突然包含foobar
. 正则表达式已更改。
$ perl -MO=Deparse scratch2.pl
sub BEGIN {
use warnings;
use strict;
use feature 'say';
overload::constant('qr', sub {
my $re = shift();
$re =~ s/\\nom/foobar/;
return $re;
}
);
}
sub match {
use warnings;
use strict;
use feature 'say';
BEGIN {
$^H{'qr'} = 'CODE(0x1e81048)';
}
my($t, $p) = @_;
$t =~ /$p/;
}
use warnings;
use strict;
use feature 'say';
BEGIN {
$^H{'qr'} = 'CODE(0x1e81048)';
}
match 'some text', qr/foobar/; # <-- here
Run Code Online (Sandbox Code Playgroud)
它甚至在代码运行之前就这样做了。
如果我们运行这两个程序来-MO=Concise
查看解释器在编译后将运行什么,我们会进一步证明这些东西只适用于源代码中的实际常量,并且不能动态工作。
$ perl -MO=Concise scratch.pl
8 <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 2529 scratch.pl:5950) v:%,R,*,&,{,x*,x&,x$,$,469762048 ->3
7 <1> entersub[t1] vKS/TARG,2 ->8
- <1> ex-list K ->7
3 <0> pushmark s ->4
4 <$> const(PV "some text") sM ->5 # <-- here
5 <$> const(PV "\\nom") sM ->6
- <1> ex-rv2cv sK/2 ->-
6 <$> gv(*match) s ->7
Run Code Online (Sandbox Code Playgroud)
与qr//
:
$ perl -MO=Concise scratch2.pl
8 <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 2529 scratch2.pl:5950) v:%,R,*,&,{,x*,x&,x$,$,469762048 ->3
7 <1> entersub[t1] vKS/TARG,2 ->8
- <1> ex-list K ->7
3 <0> pushmark s ->4
4 <$> const(PV "some text") sM ->5 # <-- here
5 </> qr(/"foobar"/) lM/RTIME ->6
- <1> ex-rv2cv sK/2 ->-
6 <$> gv(*match) s ->7
Run Code Online (Sandbox Code Playgroud)