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


I'm working on my first Perl program, and I ran into an issue. The goal is simple, pattern matching symbolic differentiation. It doesn't need to do recursive descent or anything.

The way I'm implementing it is with a recursive call to a "diff" subroutine. Here's part of my code to illustrate my problem:

#!/usr/bin/perl print diff(shift)."\n"; sub diff { my $_ = shift; # I make this local since I'll be making # recursive calls # I'll just include addition: return diff($1)."+".diff($2) if m/(.+?)\+(.+); ... # other differentiation rules here }

Here's the problem: when I recursively call diff using $1 as the argument, it will potentially do more captures and change the value of $2 by the time it gets back to do the second recursive call in the original addition. The easy fix is to name the variables under local scope:

... if (m/(.+?)\+(.+)) my $firstTerm = $1; my $secondTerm = $2; return diff($firstTerm)."+".diff($secondTerm);

But since this would come up for all of the differentiation rules, my idea was to have local copies of the captured strings on each frame of the stack (the same way I have $_):

sub diff { my $_=shift; my $1; my $2; ... }

But then I get the error:

Can't use global $1 in "my"... Execution ... aborted due to compilation errors.

Is there any way I can get it to work like this? Why do $1 and $2 behave differently than $_ in this case? Isn't $_ also "global"?



Replies are listed 'Best First'.
Re: my $1
by grinder (Bishop) on Apr 23, 2009 at 20:33 UTC

    $1 and $2 are package variables, not lexicals. You can nest their contents with local. Lexical $_ is a new addition from 5.10.0 onwards.

    Best practice is simply to copy out $1 and ilk to lexicals, the way you're doing it (but within a conditional, checking that the match succeeded).

    • another intruder with the mooring in the heart of the Perl

      Thanks for the reply.

      Ok, so could I use local for $1 and $2 in the same way that I use my for $_? This isn't working how I expected it to.

      I'm not sure if this is relevant, but the one that I'm having trouble with is the multiplication, where I use both the originals and the recursive call results:

      return "($1)*(".diff($2).")+($2)*(".diff($1).") if m/(.+?)\*(.+)/;

      This works fine using $capt1 = $1, but I'm curious as to whether it can be done using local or with the double quotes...

Re: my $1
by ikegami (Pope) on Apr 23, 2009 at 20:48 UTC
    return diff("$1")."+".diff("$2")
    The quotes create a copy of the value.

      Hmm, unless I have a different bug than I thought I did, this isn't working for me.


        You're right. I still had this on the mind. It was posted only two hours before.

        Back to your problem. There isn't one if you don't use globals. Fix:

        if (my ($pre, $post) = m/(.+?)\+(.+)/) { return diff($pre)."+".diff($post); }
Re: my $1
by almut (Canon) on Apr 23, 2009 at 20:22 UTC
    Why do $1 and $2 behave differently than $_

    Do they?

    $ perl -e 'my $_ = 1' Can't use global $_ in "my" at -e line 1, near "my $_ "

    Hint: use local for global/package variables instead (if you really need to...).

    Or, in your case with $1 etc., I think simply creating a local copy of $1 with my $capt1 = $1; would be sufficient for your recursion problem.

      perl -e 'my $_ = 1' Can't use global $_ in "my" at -e line 1, near "my $_ "

      That's just your copy of perl, which is too old :) Works fine in 5.10.

      • another intruder with the mooring in the heart of the Perl

        Baah, this gives me creeps.
        perl -e '@foo = map {$_=2; foo()} 1..10; sub foo{print}' 2222222222 $ perl -e '@foo = map {my $_=2; foo()} 1..10; sub foo{print}' 12345678910

        print "just another perl5.8 hacker"

        ...ah, you're right, I had tried with 5.8.8.

        (/me wonders what that change is supposed to be good for... :)

      Actually, my $_; works in 5.10. It is the only non-alpha variable that can be a lexical, and only a scalar.

      (Oops! got sidetracked after I started replying)

      Hi almut,

      Thanks for the reply. Interestingly,

      perl -e 'my $_=1'

      does not complain for me. I'm running v5.10.0.

      local isn't what I'm looking for in this case, but using my $capt1 = $1; worked fine. I guess I'm still perplexed about the $_ vs $1, $2, though. Any ideas?