Overloading
am 05.03.2007 12:05:48 von Ferry Bolhar
Hallo,
ich habe folgenden, eher mystisch anmutenden Code,
den ich - zunächst einmal - nur gerne verstehen würde.
Er stammt aus Regexp::MatchContext von Damian Conway
(dem Autor vom PBP).
use overload;
overload::constant
qr => sub {
my ($raw) = @_;
$raw =~ s{ \(\?
<
([^\W\d]\w*)
>
($matchcontents)
\)
}
{ push @vars, "undef\$$1;"; "($2)(?{eval'\$$1=\$^N'})" }gex
and $raw =~ s/$/|(??{@vars'(?!)'})/;
$raw =~ s/\(\?p\)/(?{\$Regexp::MatchContext::target_ref=\\\$_})/g
or $raw =~ s/\A/(?{\$Regexp::MatchContext::target_ref=undef})/;
return $raw;
};
Ohne jetzt im Detail auf das, was dieser Code macht,
näher einzugehen, geht es jetzt einmal nur um das
overload::constant qr => sub {...};
Hier wird sichtlich etwas überladen - aber was genau? qr
ist der Operator zum Erstellen eines Regex-Literals. Aber
_wann_ genau wird der diesem Operator zum Überladen
zugewiesene Code ausgeführt? Wann immer im Code ein
'qr' vorkommt? Beim Kompilieren oder erst zur Laufzeit?
Und warum hat Damian nicht gleich
use overload qr => sub {...};
geschrieben?
Und bevor die Frage kommt: ja, ich habe perldoc
overload gelesen. Aber wirklich schlau werden ich
daraus auch nicht. Gibt es zu diesem Thema vielleicht
weiterführende, etwas detailliertere Doku?
LG, Ferry
--
Ing Ferry Bolhar
Magistrat der Stadt Wien - MA 14
A-1010 Wien
E-Mail: bol@adv.magwien.gv.at
Re: Overloading
am 06.03.2007 19:34:11 von hjp-usenet2
On 2007-03-05 11:05, Ferry Bolhar wrote:
> ich habe folgenden, eher mystisch anmutenden Code,
> den ich - zunächst einmal - nur gerne verstehen würde.
> Er stammt aus Regexp::MatchContext von Damian Conway
> (dem Autor vom PBP).
>
> use overload;
> overload::constant
> qr => sub {
> my ($raw) = @_;
> $raw =~ s{ \(\?
> <
> ([^\W\d]\w*)
> >
> ($matchcontents)
> \)
> }
> { push @vars, "undef\$$1;"; "($2)(?{eval'\$$1=\$^N'})" }gex
> and $raw =~ s/$/|(??{@vars'(?!)'})/;
> $raw =~ s/\(\?p\)/(?{\$Regexp::MatchContext::target_ref=\\\$_})/g
> or $raw =~ s/\A/(?{\$Regexp::MatchContext::target_ref=undef})/;
> return $raw;
> };
>
> Ohne jetzt im Detail auf das, was dieser Code macht,
> näher einzugehen, geht es jetzt einmal nur um das
>
> overload::constant qr => sub {...};
Ich finde perldoc overload da eigentlich recht klar. Allerdings habe ich
meine Erkenntnisse nicht überprüft, kann also sein, dass die Klarheit
nur eine scheinbare ist.
> Hier wird sichtlich etwas überladen - aber was genau?
qr to overload constant pieces of regular expressions.
Also konstante Teile von Regexes. Für
m/foo${bar}baz/
würde die Funktion also zweimal, einmal mit dem Argument "foo" und
einmal mit dem Argument "baz" aufgerufen werden.
> qr ist der Operator zum Erstellen eines Regex-Literals.
Hier ist es aber kein Operator, sondern ein Key in einem Hash.
> Aber _wann_ genau wird der diesem Operator zum Ãberladen zugewiesene
> Code ausgeführt? Wann immer im Code ein 'qr' vorkommt?
Nein, bei allen regexes.
> Beim Kompilieren oder erst zur Laufzeit?
Beim Compilieren. Anderes macht bei Konstanten ja wohl wenig Sinn.
> Und warum hat Damian nicht gleich
>
> use overload qr => sub {...};
Weil er eben nicht den qr Operator überladen will, sondern konstante
Regexes.
hp
--
_ | Peter J. Holzer | Blaming Perl for the inability of programmers
|_|_) | Sysadmin WSR | to write clearly is like blaming English for
| | | hjp@hjp.at | the circumlocutions of bureaucrats.
__/ | http://www.hjp.at/ | -- Charlton Wilbur in clpm
Re: Overloading
am 07.03.2007 11:24:57 von Ferry Bolhar
Peter J. Holzer:
Erstmal danke für die Antwort - ich hatte schon Angst, mit meiner
Frage alleine zu bleiben.
> Ich finde perldoc overload da eigentlich recht klar. Allerdings habe ich
> meine Erkenntnisse nicht überprüft, kann also sein, dass die Klarheit
> nur eine scheinbare ist.
Es scheint, dass die scheinbar Klarkeit doch keine scheinbare zu sein
scheint, ;-) -> was du schreibst, stimmt.
> qr to overload constant pieces of regular expressions.
Das alleine sagt wenig - mir zumindest.
> Also konstante Teile von Regexes. Für
>
> m/foo${bar}baz/
>
> würde die Funktion also zweimal, einmal mit dem Argument "foo" und
> einmal mit dem Argument "baz" aufgerufen werden.
Ja, stimmt. Aber _das_ habe ich nirgendwo dokumentiert gefunden.
Im Prinzip wird durch das Überladen jedes Regex-Literal (also
Ausdrücke, die in m//, s//, split// oder y// verwendet oder mit dem
qr// Operator zugewiesen werden) vor der Verwendung oder
Zuweisung noch durch die Überlade-Funktion durchgejagt. Und
wie du geschrieben hast, passiert das tatsächlich für _jeden_ Teil,
wenn es mehrere gibt. Das dürfte daraus resultieren, dass intern
ein Ausdruck wie
"foo${bar}baz"
als
'foo' . $bar . 'baz'
verarbeitet wird. Daher gibt es zwei hier Konstanten, die der
Funktion zur Verarbeitung "vorgeworfen" werden. Bei
$x =~ m'foo${bar}baz';
gibt es nur eine, da durch die Verwendung der Singlequotes keine
Interpolation stattfindet (daher ist alles ein einziges Stringliteral).
Damian verwendet das in seinem Regexp::MatchContext-Modul,
um mittels des Strings '(?p)' den Ausdruck so zu markieren, dass
die Variablen $PREMATCH, $MATCH und $POSTMATCH
(bzw. die entsprechenden Funktionen) verfügbar werden.
>> qr ist der Operator zum Erstellen eines Regex-Literals.
>
> Hier ist es aber kein Operator, sondern ein Key in einem Hash.
Ja, klar. Der Key und sein Wert (die Codereferenz) wird in der
Variable %^H als ein Hashelement abgespeichert (mittels des
"overload" Pragmas). Das veranlasst den Core dann, beim
Antreffen eines Regex-Literals die entsprechende Funktion
aufzurufen.
Dieser Ansatz ist übrigens - im Gegensatz zum Überladen von
Operatoren - nicht objektorientiert und - scheinbar - auch
vollkommen unterschiedlich dazu implementiert.
>> Aber _wann_ genau wird der diesem Operator zum Überladen zugewiesene
>> Code ausgeführt? Wann immer im Code ein 'qr' vorkommt?
>
> Nein, bei allen regexes.
Genauer: bei allen Regexes, die in jenem Paket vorkommen, von dem
aus das Überladen durchgeführt wurde.
>> Beim Kompilieren oder erst zur Laufzeit?
>
> Beim Compilieren. Anderes macht bei Konstanten ja wohl wenig Sinn.
Ja und nein. Ja, weil nur eine Konstante beim Kompilieren bekannt
ist und daher von Compilercode verarbeitet werden kann. Nein, weil
ich erwartet hätte, dass mittels Overloading _jeder_ Ausdruck zur
Laufzeit auf diese Art geprüft und bearbeitet werden kann. Die Handler
von überladenen Operatoren werden ja auch erst zur Laufzeit aufgerufen.
> > Und warum hat Damian nicht gleich
> >
> > use overload qr => sub {...};
>
> Weil er eben nicht den qr Operator überladen will, sondern konstante
> Regexes.
Ja, es ist mir jetzt klar.
use overload ,;
und
use overload;
overload::constant ,;
sind vollkommen unterschiedlich implementiert. Ersteres legt im
Package, von dem aus es aufgerufen wurde, in der Symboltabelle
ziemlich kryptische Einträge an, die wohl veranlassen, dass der
Teil des Cores, der Objekte manipuliert, die dahinterliegenden
Funktionen zur Laufzeit aufruft.
Zweiteres manipuliert die Hint-Variablen $^H und %^H, was nur
beim Kompilieren von Bedeutung ist.
Ein
use overload qr => sub{...};
bewirkt vermutlich gar nichts, weil der Operator "qr" selbst nicht
überladbar ist bzw. der Core das nicht nachprüft.
Wären diese Zusammenhänge in perldoc overload in ähnlicher
Form erwähnt, wäre das Ganze wesentlich klarer. Aber wie ja
schon der Autor am Schluss selber schreibt:
"This document is confusing. There are grammos and
misleading language used in places. It would seem a total
rewrite is needed."
Dem ist nichts hinzuzufügen.
Danke dir nochmals für die Erklärung. Ach ja, eine Kleinigkeit
noch: was macht den nun der mit "push @vars" beginnende
Code, der als Replacement des Substitutionsbefehls beginnt?
LG, Ferry
--
Ing Ferry Bolhar
Magistrat der Stadt Wien - MA 14
A-1010 Wien
E-Mail: bol@adv.magwien.gv.at
Re: Overloading
am 07.03.2007 13:49:16 von Ferry Bolhar
Ich schrieb:
>> Nein, bei allen regexes.
>
> Genauer: bei allen Regexes, die in jenem Paket vorkommen, von dem
> aus das Überladen durchgeführt wurde.
Stimmt so auch nicht:
use overload ;
ist (paket-) global, da die jeweilige Symboltabelle modifiziert wird.
overload::constant (und das Gegenstück overload::remove_constant)
hingegen sind, da sie die Variablen $^H und %^H beeinflussen,
lexikalisch:
use overload;
sub do_over {
my $regex = shift;
$regex =~ s/Halli/Hallo/g;
$regex;
}
BEGIN {overload::constant(qr => \&do_over)} # Aktivieren
my $x = 'Hallo';
print "1 Passt!\n" if $x =~ /Halli/; # Wird zu
/Hallo/, matcht
{ #
Neuer Scope
BEGIN {overload::remove_constant('qr')} # Deaktivieren
print "2 Passt!\n" if $x =~ /Halli/; # "Halli" bleibt,
match nicht
} #
Scope-Ende, overload wieder aktiv
print "3 Passt!\n" if 'Hallo' =~ /Halli/; # Match wieder.
Hier bekommt man
1 Passt!
3 Passt!
ausgegeben. Kommentiert man den inneren BEGIN-Block
aus, so kommt auch noch ein "2 Passt!" dazu. Tja, wer hätte
gedacht, dass
'Hallo' =~ /Halli/
wahr ist? ;-))
IMHO wäre es schön, wenn "overload::constant" und
"overload::remove_constant" auch als lexikalische Pragmas
verfügbar wären, aber wahrscheinlich ist der Bedarf hierfür
nicht gegeben.
LG, Ferry
--
Ing Ferry Bolhar
Magistrat der Stadt Wien - MA 14
A-1010 Wien
E-Mail: bol@adv.magwien.gv.at