当我使用字符串变量时,为什么不是我的重载::常量子触发?

时间:2017-03-13 14:23:12

标签: perl

我正在尝试overload constants in regular expressions。这是我的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;

这是我想要触发重载的子程序:

sub ChopPattern {
    my $string= shift;
    my $pattern = shift;

    if($string =~ m/$pattern/) {
        $string =~ s/$&/ /g;
        return ($string, $&);
    } else {
        return ($string, '');
    }
}

这是我的测试:

$test = "foo bar Max Fast bar foo";
($test, $name) = ChopPattern($test, '\nom');
say $test;
say $name;

如果我在子程序的匹配中硬连线测试模式\nom

sub ChopPattern {
    my $string= shift;
    my $pattern = shift;

    if($string =~ m/\nom/) {
        $string =~ s/$&/ /g;
        return ($string, $&);
    } else {
        return ($string, '');
    }
}

测试得出正确的答案:

foo bar  bar foo
Max Fast

但如果我在匹配中使用$pattern,则测试结果为:

foo bar Max Fast bar foo
<null line>

是否有\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

2 个答案:

答案 0 :(得分:5)

  

是否有\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

你正在做的事情是个坏主意。以下实现更容易理解,并且不会在任何地方改变正则表达式语义:

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>";

输出:

<foo  qux> <Bar Baz >

我猜你是因为想要处理不止一个魔法而超负荷工作的#34;字符串(例如\nom)。我用一个简单的哈希将字符串映射到正则表达式。

答案 1 :(得分:4)

编程Perl 表示overload::constant适用于常量。

  

只要Perl tokener遇到常数,就会调用为integer和float提供的任何处理程序。

当你致电m/$pattern/时,那不是常数。它是一个变量。

($test, $name) = ChopPattern($test, '\nom');

现在'\nom'是常量,但它是一个字符串。将其转换为qr//,您将拥有一个包含常量的正则表达式。

($test, my $name) = ChopPattern($test, qr'\nom');

ChopPattern中的模式匹配可以保持不变:

if($string =~ m/$pattern/) { ... } 

因为现在正则表达式中有一个常量部分,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' );

代码的作用并不重要。当我们解析它时,我们得到这个输出:

$ 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

我们可以看到处理程序已安装,但在函数调用的最后一行中,有'\\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/ );

现在,已解除程序突然包含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

在代码运行之前就已经这样做了。

如果我们用-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

使用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