[Dresden-pm] Regul�re Ausdr�cke - Suchen und Ersetzen von Worten

Aristoteles Pagaltzis pagaltzis at gmx.de
So Aug 24 22:18:23 PDT 2008


* Torsten Knorr <create-soft at tiscali.de> [2008-08-23 14:20]:
> Version: Aristoteles leicht optimiert produziert aber ungültiges HTML
> $text =~ m{$rx(?{
> 	push(@m, [$-[0],  "<a href=\"$1.html\">"]);
> 	push(@m, [$+[0], '</a>']);
> 	})(?!)}xi;
> for(sort { $a->[0] <=> $b->[0] } @m)
> 	{
> 	$new	.= substr($text, $pos, $_->[0] - $pos) . $_->[1];
> 	$pos	= $_->[0];
> 	}
> $new		.= substr($text, $pos);

Und das findest du ehrlich verständlicher und eingängiger als den
Code, den ich geschrieben habe?

Außerdem wage ich die Optimierung stark anzuzweifeln:

    $ ( echo; perl -Mre=debug -le'/\bfoo\b/'; echo; \
    > perl -Mre=debug -le'/\b(?:foo|bar)\b/' )2>&1|grep -v ^Freeing

    Compiling REx `\bfoo\b'
    size 5 Got 44 bytes for offset annotations.
    first at 1
       1: BOUND(2)
       2: EXACT <foo>(4)
       4: BOUND(5)
       5: END(0)
    anchored "foo" at 0 (checking anchored) stclass "BOUND" minlen 3 
    Offsets: [5]
        1[2] 3[3] 0[0] 6[2] 8[0] 

    Compiling REx `\b(?:foo|bar)\b'
    size 10 Got 84 bytes for offset annotations.
    first at 1
       1: BOUND(2)
       2: BRANCH(5)
       3:   EXACT <foo>(9)
       5: BRANCH(8)
       6:   EXACT <bar>(9)
       8: TAIL(9)
       9: BOUND(10)
      10: END(0)
    stclass "BOUND" minlen 3 
    Offsets: [10]
        1[2] 5[1] 6[3] 0[0] 9[1] 10[3] 0[0] 12[0] 14[2] 16[0] 

Siehst du dieses »anchored "foo"«? Das bedeutet, daß die
Regex-Engine mit dem einfachen Regex erst gar nicht anspringt,
wenn eine Boyer-Moore-Suche diesen String darin nicht finden
kann. Beim Suchstring »foo« können dabei dann meistens pro
Schleifendurchgang zwei Zweichen überschlagen werden.

Die Variante, die versucht, beide Strings auf einmal zu finden,
ist nicht optimierbar und erzwingt die Verwendung der Regex-
Engine, die den String Zeichen für Zeichen abgrast. Als ob das
nicht genug wäre, erzwingt das Pattern auch noch ein Backtracking
wegen des `|`. Unterm Strich wird also beim Versuch, das zweite
Pattern zu machten, jedes einzelne Zeichen (mindestens!) zweimal
angefasst. (Mindestens, weil ein teilweiser aber fehlgeschlagener
Match mehrere folgende Zeichen auch noch anfasst.)

MaW, wenn du `/\b(?:foo|bar)\b/` schreibst müssen dreimal so
viele Zeichen angefasst werden wie wenn du `/\bfoo\b/ or
/\bbar\b/` schreibst. Bei Schlagwörtern, die länger sind als nur
drei Zeichen, wird das ganze nur noch unverhältnismässiger.

Mal davon ganz abgesehen leidet die Lesbarkeit des Codes massiv
unter dem Versuch, `(?!)` einzusetzen. Du hast den Code also
unleserlich und langsam gemacht. Es sieht aber sehr viel cleverer
aus! Die Vorurteile gegen Perl verwundern mich nicht, wenn ich so
etwas sehe.

Gruß,
-- 
Aristoteles Pagaltzis // <http://plasmasturm.org/>


Mehr Informationen über die Mailingliste Dresden-pm