Search code examples
regexperlcompilationmodifier

How do you force Perl to re-compile a regex compiled with "/o" on demand?


Technical question:

Given a regex:

my $regEx = qr{whatever$myVar}oxi; # Notice /o for "compile-once"

What is the most effective way to force it to recompile on demand? (e.g. when I know from the program logic that $myVar value changed) without dropping /o and depending on Perl's internal smarts to auto-recompile?

NOTE: The regex is used in a substitution, which may affect re-compilation rules sans /o:

$string2 =~ s/$regEx//;

The context is:

  • I have a regular expression that is built by slurping in a fairly long (>1k long) string from a config file.

    • That file is re-read once every 60 minutes.

    • If the string read from the file changes (as defined by changing file timestamp), I want to re-compile the regex using the re-slurped string value in $myVar.

  • The regex is used repeatedly and frequently in the Perl module running under mod_perl.

    • This means that (coupled with the string being >1-2k long) I must use "/o" modifier to force compile-once on the regex, to avoid performance hit of Perl repeatedly checking if the variable value changed (this heuristic is from perlop qr//, since the regex is used as part of s/// as shown above and not by itself as a match).

    • That in turn means that, when I know that the variable changed after re-slurping it in 1 hour, I need to force the regex to re-compile despite the /o modifier.

UPDATE: Here's an illustration of why I need /o - without it, the regex is recompiled (and thus necessarily checked) every loop iteration; with it it is NOT:

$ perl -e '{for (my $i=0; $i<3; $i++) {
                 my $re = qr{$i}oix; $s="123"; $s =~ s/$re//; 
                 print "i=$i; s=$s\n"; }}'
i=0; s=123
i=1; s=123
i=2; s=123

$ perl -e '{ for (my $i=0; $i<3; $i++) { 
                  my $re = qr{$i}ix; $s="123"; $s =~ s/$re//; 
                  print "i=$i; s=$s\n"; }}'
i=0; s=123
i=1; s=23
i=2; s=13

Solution

  • when I know from the program logic that $myVar value changed

    m//, s/// and qr// only compile if the pattern doesn't change. All you have to do to get the behaviour you requested is to remove the /o.

    $ perl -Mre=debug -e'
        qr/$_/ for qw( abc abc def def abc abc );
    ' 2>&1 | grep Compiling
    Compiling REx "abc"
    Compiling REx "def"
    Compiling REx "abc"
    

    Therefore,

    If the string read from the file changes (as defined by changing file timestamp), I want to re-compile the regex using the re-slurped string value in $myVar.
    my $new_myVar = ...;
    if ($myVar ne $new_myVar) {
       $re = qr/$new_myVar/;
       $myVar = $new_myVar;
    }
    ...
    s/$re/.../
    

    or just

    $myVar = ...;
    ...
    s/$myVar/.../