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

use*_*346 10 perl

我正在尝试在正则表达式中重载常量.这是我的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)

Thi*_*Not 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
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).我用一个简单的哈希将字符串映射到正则表达式.


sim*_*que 4

《Programming Perl》说这overload::constant适用于常量。

每当 Perl 记号器遇到常量时,您为整数和浮点数提供的任何处理程序都会被调用。

当您调用 时m/$pattern/,这不是一个常量。这是一个变量。

($test, $name) = ChopPattern($test, '\nom');
Run Code Online (Sandbox Code Playgroud)

现在,这'\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)