2017-03-13 4 views
10

Ich versuche overload constants in regular expressions. Hier ist mein Tagger Paket:Warum wird mein overload :: constant sub nicht ausgelöst, wenn ich eine Stringvariable verwende?

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; 

Hier ist das Unterprogramm, in dem Ich mag würde die Überlastung auslösen:

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

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

Hier ist mein Test:

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

Wenn ich verdrahten das Testmuster, \nom, in der Partie des Unterprogrammes:

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

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

der Test ergibt die richtige Antwort:

foo bar bar foo 
Max Fast 

Aber wenn ich $pattern im Spiel, wie oben dem Testergebnisse:

foo bar Max Fast bar foo 
<null line> 

Gibt es einen Grund dafür? \nom löst Tagger aber eine Variable gleich \nom nicht aus?

Hier sind die Details der Version von Perl verwendet:

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 
+0

Ein paar Upvotes wert, da es mir nie in den Sinn gekommen ist, die Regex-Engine so zu überladen. – Sobrique

+2

@Sobrique: Es ist möglich, aber es ist nicht eine sehr nette Sache zu tun. Fernwirkung und all das. – Borodin

+0

Das hatte es vorher auch nicht gesehen. Zuerst dachte ich, sie hätten falsch verstanden was "Überladung" tut. Aber es ist eigentlich ziemlich logisch. Ich sehe einfach keinen Anwendungsfall dafür. Es ist verrückt. – simbabque

Antwort

4

Programming Perl sagt, dass overload::constant Arbeiten auf Konstanten.

Alle Handler, die Sie für Integer und Float bereitstellen, werden aufgerufen, wenn der Perl-Tokener auf eine konstante Zahl stößt.

Wenn Sie anrufen m/$pattern/, das ist keine Konstante. Es ist eine Variable.

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

Jetzt gibt es die '\nom' eine Konstante, aber es ist eine Zeichenfolge. Verwandle das in eine qr// und du wirst einen regulären Ausdruck haben, der eine Konstante enthält.

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

Das Muster-Match in ChopPattern gleich bleiben kann:

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

Da es nun ein konstanter Teil in einem regulären Ausdruck ist, kann Perl rufen Sie Ihre convert Überlastung und Ihre regex tun.


Lassen Sie uns dies in Aktion sehen. Denken Sie daran, dass Perl diese Ersetzung während der Kompilierung überlastet, wenn der Quellcode analysiert wird.

Betrachten Sie dieses Beispiel:

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'); 

Es ist nicht wichtig, was der Code tut. Wenn wir es Deparse, bekommen wir diese Ausgabe:

$ 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 

Wir können sehen, dass der Handler installiert wurde, aber in der letzten Zeile im Funktionsaufruf gibt es den '\\nom' String.

Wenn wir jetzt einen zitierten Ausdruck qr// dort anstelle der Zeichenfolge verwenden, ändern sich die Dinge.

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/); 

Jetzt enthält das entartete Programm plötzlich foobar. Die Regex wurde geändert.

$ 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 

Es tat, bevor der Code überhaupt ausgeführt wurde.

Wenn wir beide Programme mit -MO=Concise ausführen, um zu sehen, was der Interpreter nach der Kompilierungszeit ausführen wird, erhalten wir einen weiteren Beweis, dass dieser Vorgang nur mit tatsächlichen Konstanten im Quellcode funktioniert und nicht dynamisch arbeiten kann.

$ 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 

Und mit 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 
5

Gibt es einen Grund, dass \nom löst Tagger, sondern eine Variable gleich \nom nicht?

Da '\nom' ein Zeichenfolgenliteral ist, keine Konstante Stück eines regex:

$ 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 

Was Sie ist eine schlechte Idee zu tun. Die folgende Implementierung ist viel einfacher zu verstehen und nicht regex überall Semantik ändern:

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

Ausgang:

<foo qux> <Bar Baz > 

Ich vermute, Sie mit Überlastung ging, weil Sie mehr als eine handhaben wollen " Magic "String (zB \nom). Ich habe das mit einem einfachen Hash gemacht, der Strings auf Regexes abbildet.

Verwandte Themen