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

I am trying to wrap my mind around dynamic regular expressions, and am having problems. I am trying to see how to get a recursive expression using (??{}), so I am trying to write a simple case for matching an increasing sequence of numbers, i.e. 23456. I have tried

perl -e "my $c; $c = qr/ ( \d | (??{ $c++ }) )* /x; '2345654321' =~ $c; print $&;"

I get 2345654321. I wanted 23456.

Is there a problem with the scope of $c inside the expression? Is it that the * will just match all the \d it sees?

I have read perlre, Mastering Regular Expressions 2nd ed. Chapter 7, and searched the net on dynamic regexes, and cannot find sufficient explanations.

I am limited to Perl 5.8 and no, I would never use $& in anything but a one-liner.

Replies are listed 'Best First'.
Re: Regex to match ascending sequence
by LanX (Saint) on Oct 15, 2015 at 16:43 UTC
    Second statement: assign regex to $c

    Inside regex: increment $c

    what do you expect from an incremented regex?

    Then your regex says greedy match ( any number  or  $c++ ) and number will match any time before trying the or branch.

    Actually this might be the reason why incrementing a regex doesn't throw an error, it's never executed.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      Sadly true. Indication that at this point I am flailing at trying to figure out how to do it.
Re: Regex to match ascending sequence
by Eily (Monsignor) on Oct 15, 2015 at 16:59 UTC

    What did you expect $c++ to do, knowing that the first time the code is run the value of $c is the current regular expression ?

    Anyway, here is something that should do what you want:

    '234565432' =~ / (\d) (?{ $c = $1}) # Match the first number and initialize $c (?: (??{ ++$c }) # Same as the previous char + 1 )*/x; print $&;

      Saints be praised! OK, maybe Curates be praised! It works. And it makes sense. Can't beat that. I can feel scales of ignorance falling off my eyes as I type.
      Try 148012 :)
        > Try 148012 :)

        This should do

        my $regex = qr/ \d (??{ substr($&, -1) + 1 }) + /x; "148012" =~ $regex; print ">$&<" __END__ >012<

        The minimal length of the sequence can be set by {x,} instead of the + quantifier. ¹

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

        ¹) with x = min-1

        I think that it is to be expected with left-most, longest. It would find the 1, not find a 2 after it and quit, without seeing that there is a 012 later.
Re: Regex to match ascending sequence
by Laurent_R (Canon) on Oct 15, 2015 at 16:40 UTC
    In your regex, I guess that \d* will match the whole string so that the other part of the alternation is just short circuited.
Re: Regex to match ascending sequence
by LanX (Saint) on Oct 15, 2015 at 17:02 UTC
    This works for me

    my $regex = qr# (\d) (??{ my $c //= $1 ; ++$c })+ #x; "2345654321" =~ $regex; print $&;

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      Unfortunately, when I try this on my Perl 5.8, I get a panic: top_env message. I know that it doesn't have the //= operator.
        $c //= $d;
        is just syntactic sugar for various constructs such as:
        $c = defined $c ? $c : $d;
        or
        $c = $d unless defined $c;
        Update: I've just quickly tried on an old VMS platform with Perl 5.8.6 with this regex definition:
        $regex = qr# (\d) (??{ my $c = $1 unless defined $c; ++$c })+ #x;
        it still does not seem to work.
        The Defined-Or operator is best emulated with defined and or

        No idea about scopes in 5.8 , I remember 5.10 (or 5.12 ?) having an issue in this case.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

        Try  local our $c;

        Also, 5.8 is too old for experimental regex features

Re: Regex to match ascending sequence
by Anonymous Monk on Oct 15, 2015 at 16:49 UTC
    perl -le '"2345654321" =~ /(0?1?2?3?4?5?6?7?8?9?)/ and print $1'

    snicker :)

      That works for what I stated, but not for what I _meant_. It would match 1357, but I would want it to match 1234567. Also, I am looking for a dynamic regex to do this. The whole reason for all this is that I am trying to get a grasp on dynamic regexes (??{}) and thought this would be an easy start.