http://www.perlmonks.org?node_id=925326

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

> perl -v This is perl, v5.10.1 (*) built for i686-cygwin-thread-multi-64int (with 13 registered patches, see perl -V for more detail) Copyright 1987-2009, Larry Wall Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using "man perl" or "perldoc perl". If you have access to + the Internet, point your browser at http://www.perl.org/, the Perl Home Pa +ge. > cat tmp.pl #!/usr/bin/perl sub mysub { die 'single word expected' if (my $name = shift) !~ /^\w+$/; die 'single word expected' if (my $value = shift) !~ /^\w+$/; } $str = "abc def"; $str =~ /(\w+)\s+(\w+)/; mysub($1, $2); > ./tmp.pl single word expected at ./tmp.pl line 4.
perlvar says "read-only and dynamically scoped"
$<digits> ($1, $2, ...) Contains the subpattern from the corresponding set of capturing pa +rentheses from the last successful pattern match, not counting patter +ns matched in nested blocks that have been exited already. (Mnemonic: + like \digits.) These variables are all read-only and dynamically sco +ped to the current BLOCK.
I'm confused a bit
have decided to post here

Replies are listed 'Best First'.
Re: unexpected: inside sub($1,$2) if do // then arguments will be changed
by repellent (Priest) on Sep 11, 2011 at 08:23 UTC
    In mysub(), aliases to the global variables $1 and $2 are held in @_, but those globals don't stay set as you expected for very "long":
    use warnings; use Data::Dumper; sub mysub { warn Dumper \@_; # globals still set die 'single word expected' if (my $name = shift) !~ /^\w+$/; warn Dumper \@_; # you "lost" the globals (due to matching /^ +\w+$/) die 'single word expected' if (my $value = shift) !~ /^\w+$/; } $str = "abc def"; $str =~ /(\w+)\s+(\w+)/; mysub($1, $2); __END__ $VAR1 = [ 'abc', 'def' ]; $VAR1 = [ undef ]; Use of uninitialized value $value in pattern match (m//) at ./t.pl lin +e 12. single word expected at ./t.pl line 12.

    Save off global variables, or avoid using them:
    my ($name, $value) = ($str =~ /(\w+)\s+(\w+)/); mysub($name, $value);
Re: unexpected: inside sub($1,$2) if do // then arguments will be changed
by JavaFan (Canon) on Sep 11, 2011 at 10:19 UTC
    Note that this issue (already explained by others) isn't limited to $1 and frieds. It can happen to other variables as well, including lexical scoped variables:
    use 5.010; use strict; use warnings; my($foo, $bar) = (1, 2); sub baz { say shift; $bar = 3; say shift; } baz $foo, $bar; __END__ 1 3
Re: unexpected: inside sub($1,$2) if do // then arguments will be changed
by ikegami (Patriarch) on Sep 12, 2011 at 05:05 UTC

    perlvar says "read-only and dynamically scoped"

    They are. mysub does indeed back them up on entry and restore them on exit.

    $ perl -E' sub f { say $1; "b"=~/(b)/; say $1; } "a"=~/(a)/; say $1; f(); say $1; ' a a b a

    The problem is that $_[0] and $_[1] are aliased to $1 and $2, so changing $1 and $2 changes $_[0] and $_[1].

    $ perl -E' sub f { say $_[0]; "b"=~/(b)/; say $_[0]; } "a"=~/(a)/; f($1); ' a b

    Three solutions:

    • /.../; mysub("$1", "$2"); sub mysub { /.../; shift; shift; }
    • my ($x, $y) =~ /.../; mysub($x, $y); sub mysub { /.../; shift; shift; }
    • /.../; mysub($1, $2); sub mysub { my ($x, $y) = @_; /.../; $x; $y; }
Re: unexpected: inside sub($1,$2) if do // then arguments will be changed
by Anonymous Monk on Sep 11, 2011 at 07:42 UTC
    Global variables are global :) copy them mysub( "$1", "$2" );
Re: unexpected: inside sub($1,$2) if do // then arguments will be changed
by Khen1950fx (Canon) on Sep 11, 2011 at 08:52 UTC
    (Mnemonic: like \digits)

    Try it like this: \(&mysub(<$1> <$2>));

    #!/usr/bin/perl -slw use strict; use re 'debug'; sub mysub { die 'single word expected' if (my $name = shift @_) =~ /^\w+$/; die 'single word expected' if (my $value = shift @_) =~ /^\w+$/; } my $str = 'abc def'; $str =~ /(\w+)\s+(\w+)/; print "\(&mysub(<$1> <$2>))";

        It's a dressed up version of this:

        print "'$1' '$2'";