in reply to What's like $+ but not gives the ordinal?

If you want the number of the backreference and you know in advance the number of possible backreferences, here's a quick hack:

#!/usr/bin/perl -w use strict; my $string = 'abcbar'; my $sub = get_backref( $string ); print $sub; sub get_backref { my $string = shift; my $regex = '(foo)|(bar)|(baz)'; my $backref = 0; # This is the number of possible backreferences # It's easier to set than generate dynamically my $limit = 3; local ( $1, $2, $3 ); $string =~ /$regex/; for ( 1 .. $limit ) { no strict 'refs'; $backref = $_, last if defined $$_; } return $backref; }

Update: Here's a much more robust example (though with no validation of arguments to the sub). Pass the string, the number of backreferences, and the regex and it will return to you the regex that matched:

#!/usr/bin/perl -w use strict; my $string = 'abcbar'; my $sub = get_backref( $string, 3, '(foo)|(bar)|(baz)' ); print $sub; sub get_backref { my ( $string, $num_refs, $regex ) = @_; my $backref = 0; # This is the number of possible backreferences # easier to set than count my $local_brefs = ''; for ( 1 .. $num_refs ) { $local_brefs .= "\$$_,"; } chop $local_brefs; my $code = <<" END_OF_CODE"; local ( $local_brefs ); \$string =~ /$regex/; for ( 1 .. $num_refs ) { no strict 'refs'; \$backref = \$_, last if defined \$\$_; } END_OF_CODE eval $code; return $backref; }

Of course, it returns the number of the backreference that matched first (i.e., if $2 matched first, it returns 2). It returns zero if no match is found. You'll have to test for this if you're using it as an array index unless $array[0] is a default.

Cheers,
Ovid

Vote for paco!

Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Replies are listed 'Best First'.
Re: (Ovid) Re: What's like $+ but not gives the ordinal?
by John M. Dlugosz (Monsignor) on Jun 28, 2001 at 01:51 UTC
    I don't understand why you need to localize the backref variables. Also, you're finding the first capture, not the last.

    Here is a way that finds the max by itself, as implied by another message on this thread:

    sub last_paren_match_ordinal() { my $n= scalar @+; # gives number of captures present. while ($n) { no strict 'refs'; last if defined $$n; --$n; } return $n; }
    In your new code, you are localizing the same name as you are my-ing. What's that for? It's not even used inside the block where it's localized.

    —John

      John, this stuff gets a bit tricky because what I've done is write a code generator.

      I don't understand why you need to localize the backref variables.

      Because if the match fails on the current attempt, but it succeeded on a previous attempt, the backref variables ($1, $2, etc) will contain the values from the previous match. The following code demonstrates this:

      my $string = '1234'; $string =~ /(2)/; $string =~ /(a)/; print $1;
      Also, you're finding the first capture, not the last.

      I misread your post then. Make the following change:

      - for ( 1 .. $num_refs ) { + for ( $num_refs .. 1 ) {
      Here is a way that finds the max by itself...

      I didn't know about the @+ variable :)

      In your new code, you are localizing the same name as you are my-ing.

      Actually, I'm not. Before the eval statement, add the statement print $code;. That will show you what's going on. The HERE document is a scalar containing generated code to be evaled. That's all.

      As I mentioned, my code is probably not worth the effort as I did not know about @+. :)

      Update 2: Duh! Of course $num_refs .. 1 isn't going to work. Sigh.

      Cheers,
      Ovid

      Vote for paco!

      Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

        I guess there is a lot of powerful stuff going on there that I don't follow.

        - for ( 1 .. $num_refs ) { + for ( $num_refs .. 1 ) {
        That's not what happens when I try it. For example,
        perl -e "$n=5; for($n..1) { print }"
        does not print the reverse of
        perl -e "$n=5; for(1..$n) { print }"