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

Hello all,

So I have a few global variables declared at the top of my script, call them "red", "blue" and "yellow" for instance, and I have a subroutine that takes in these variables and does some computation and updates them to that value. I made a few variables global because I'm writing a rather large script and these are just the values I need to compute on rather frequently so I thought that'd just be easiest.

so essentially I have...

#!/usr/bin/perl my $red = undef; my $yellow = undef; my $blue = undef; sub do_something { # some computation $_[0] = (some new value); $_[1] = (some new value); $_[2] = (some new value); } ... do_something(color1, color2, color3);

This works just fine, but I would really like to improve readability because I have a few other larger modules of this form (with more arguments) and to be honest I'm not happy with the way it looks. Its just not to clear what $_[0] is or what $_5 is without looking up what the arguments are for that routine.

So what I'd like to do is rename them somehow within the function to make things more easily understandable.

Something like this, but not quite

sub do_something { my ($red, $yellow, $blue) = @_; # some computation $red = (some new value); $yellow = (some new value); $blue= (some new value); }

I realize "my" is lexical scope so its only visible within that block and doing that would just make a copy of the argument value to the new block variable defined and the actual argument variable doesn't get changed/updated, only passed as reference.

I know I can define other variables out of the scope and just assign them inside but that's just not necessary as I'll only be using them once inside some subroutine for clarity's sake. I just need a way to give a name to the passed in values "temporarily".

So, is there a simple way to just assign a temporary "alias" to the global arguments?

I would guess this would be a fairly easy problem for all of you. I'm very much still a perl newb and not much of a programmer so I would be totally gracious if you could give me a quick tip. (I did do a google search but couldn't find a good answer)

Replies are listed 'Best First'.
Re: how to "rename" a global argument in subroutine?
by stevieb (Canon) on Aug 11, 2015 at 18:30 UTC

    A subroutine in Perl takes its parameters and flattens them into a single list, and implicitly puts them in an array called @_. $_[N] is the Nth element of the @_ parameters array.

    The following simply assigns those elements to names:

    sub do_something { my ($red, $yellow, $blue) = @_; ... }

    To do what you want, you'll need to pass by reference, dereference the items within the sub and then act on them:

    my ($red, $blue, $green) = qw(1 1 1); sub do_something { my ($red, $blue, $green) = @_; $$red += 1; $$green += 30; } do_something(\$red, \$blue, \$green); print "$red, $blue, $green\n";

    It may be easier for you to define a single hash with all of your globals instead of individual scalars, and just pass that around via reference:

    my %hash = ( red => 1, blue => 1, green => 1, ); sub do_something { my $href = shift; $href->{red} += 1; $href->{green} += 30; } do_something(\%hash); print "$hash{red}, $hash{blue}, $hash{green}\n";

    Note that the longer the script gets, the harder it is to keep track of globals, especially when they get modified at-a-distance unexpectedly.

    -stevieb

      I see! Thank you for your informative post :)
Re: how to "rename" a global argument in subroutine?
by FreeBeerReekingMonk (Deacon) on Aug 11, 2015 at 18:23 UTC

    Parse by reference:

    my $red = undef; my $green = undef; my $blue = undef; sub change{ my($color1, $value1, $color2, $value2, $color3, $value3) = @_; ${$color1} = $value1; ${$color2} = $value2; # maybe: if defined $value2; ${$color3} = $value3; } change(\$red, 42, \$green, 255, \$blue, 11); print "($blue)";

    Alternative:

    #!/usr/bin/perl use strict; use warnings; my %COLORS = ( red => undef, green => undef, blue => undef, ); sub change2{ my($R) = @_; my %H = %$R; for my $key (keys %H){ $COLORS{$key} = $H{$key}; } } change2( {red=>5, green=>6, blue=>7} ); print $COLORS{red};

      And if those RGB variables are "static", then why not put them into a nice hash?

      #!/usr/bin/perl use strict; use warnings; my %COLORS = ( red => undef, green => undef, blue => undef, ); sub change{ my($R,$G,$B) = @_; $COLORS{red} = $R; $COLORS{green} = $G; $COLORS{blue} = $B; } change(11); print $COLORS{red};
        I hadn't known about hashing! Thanks for your nice post!

      Thank you for your answer! ...although I'm now having some other issue related..

      Does "parsing by reference" make that a symbolic link? Because later when I try to access $red 's value I get the following error.

      "Can't use string ("red") as a SCALAR ref while "strict refs"

      Would you have any other advice on how I could go about resolving this? Much appreciated.

        Yes, a reference is a "symbolic link", to point to the value you need to either use the arrow or typecast it. For example:

        #!/usr/bin/perl use strict; use warnings; my $red = 23; my $also_red = \$red; ${ $also_red } = 12; # typecast to scalar using ${} print "$red is actually $$also_red"; # this also works
Re: how to "rename" a global argument in subroutine?
by RonW (Parson) on Aug 12, 2015 at 17:49 UTC

    Note that Perl (implicitly) passes parameters by reference. From perlsub:

    The array @_ is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element $_[0] is updated, the corresponding argument is updated (or an error occurs if it is not updatable).