neybar has asked for the wisdom of the Perl Monks concerning the following question:

I have a regex that matches parts of a date and then sticks the matches into $year $month and $day variables. In stead of using the standard $1, $2, $3 variables I wanted to play with embedding perl code into the regex.

The regex works just great on a single pass. If I stick the regex in a sub and access it more than once, then the $year,$month and $day variables only get filled on the first time it is accessed. Here is code:

#!/usr/bin/perl use Data::Dumper; use strict; #use re 'debug'; my $foo_year = "2004-11-12 12:40:42"; test($foo_year); test($foo_year); test($foo_year); sub test { my ($date) = @_; print $date,"\n"; my ($year,$month,$day); $date =~ m/ (\d+) (?{ $year = $^N }) - (\d+) (?{ $month = $^N }) - (\d+) (?{ $day = $^N }) /x; print "$year-$month-$day\n"; }

It seems like $^N isn't getting set on subsequent calls to the regex.

Replies are listed 'Best First'.
Re: regex and embedded perl code
by ikegami (Patriarch) on Feb 07, 2006 at 19:28 UTC

    It sounds like the (?{ ... }) blocks are captures. They always referer to the $year, $month and $day variables created by the first call to test. If that's the case, then the solution would be to avoid using lexical (my) variables. The following is an untested (Update: tested) fix:

    sub test { my ($date) = @_; print $date,"\n"; our ($year, $month, $day); local (*year, *month, *day); $date =~ m/ (\d+) (?{ $year = $^N }) - (\d+) (?{ $month = $^N }) - (\d+) (?{ $day = $^N }) /x; print "$year-$month-$day\n"; }

    I'm assuming this is just a simplifciation of your problem. If not, don't use experiemental features for nothing. Use something like the following instead:

    sub test { my ($date) = @_; print $date,"\n"; my ($year, $month, $day) = $date =~ m/(\d+)-(\d+)-(\d+)/; print "$year-$month-$day\n"; }

    Of course, you should check if the regexp actually succeeded before using the date components. None of the snippets do this.

    Update: Neither yours nor my first snippet work in Perl 5.6.1, but that's because $^N didn't exist before Perl 5.8.0.

Re: regex and embedded perl code
by GrandFather (Saint) on Feb 07, 2006 at 19:54 UTC

    The issue is that the compilation of the (?{...}) in the regex creates closures on $year, $month and $day and the second time through the sub the my ($year, $month, $day); creates different instances of the variables which are not the ones the regex is using. Change my ($year,$month,$day); to our ($year,$month,$day);.


    DWIM is Perl's answer to Gödel
Re: regex and embedded perl code
by borisz (Canon) on Feb 07, 2006 at 22:25 UTC
    Another solution, if you want $year, $month, $day in lexical context and assign the data with $^N use a closure.
    #!/usr/bin/perl use strict; my $foo_year = "2004-11-12 12:40:42"; test($foo_year); test($foo_year); test($foo_year); { my ( $year, $month, $day ); sub test { my ($date) = @_; print $date, "\n"; $date =~ m/ (\d+) (?{ $year = $^N }) - (\d+) (?{ $month = $^N }) - (\d+) (?{ $day = $^N }) /x; print "$year-$month-$day\n"; } } __OUTPUT__ 2004-11-12 12:40:42 2004-11-12 2004-11-12 12:40:42 2004-11-12 2004-11-12 12:40:42 2004-11-12
    Boris
Re: regex and embedded perl code
by shotgunefx (Parson) on Feb 07, 2006 at 22:53 UTC
    Another problem is your not conditionally assigning on a match. I got bitten by this myself once (Regex Capturing: Is this a bug or a feature?)

    Say on the second pass, you feed it the string

    "2001-4 foo bar"

    The result will be

    "1-11-12"

    as you're blindly assigning. Even better, pass it "" the second time around and you'll get

    2004-11-12


    -Lee

    perl digital dash (in progress)
Re: regex and embedded perl code
by Boyd (Initiate) on Feb 07, 2006 at 23:08 UTC
    If you wanted to learn to use embedded Perl, this is helpful to me, too. But to get around using $1,$2,$3, I'm sure you know you can use:
    my( $year, $month, $day ) = ( $date =~ m{ (\d+) - (\d+) - (\d+) }x );
    Just threw this in for completeness. Boyd