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

I have been pondering this for a few days now and I always seem to run into a problem when I want to use strict (which every good perl programmer should) and I have the first use of a variable inside of a loop or if statement. If I want to later print that variable or refer to it some other way I have to either declare it as a global variable or pass the results out of the specific function.

For Example:

#!/usr/bin/perl -w use strict; die "Required argument missing!\n" unless ($ARGV[0]); if ($ARGV[0] =~ /Hello/i) { my $response = "Hello to you.\n"; }else{ my $response = "Huh?\n"; } print $response;
Now if I syntax check this or run it I would get the following error.

Global symbol "$response" requires explicit package name at syn_check line 11. syn_check had compilation errors.
What is the best way to pass the variable $response out of the if statement to make it visible at another level? Remember I do not want to make the variable global either.

Replies are listed 'Best First'.
Re: Best way to pass variables?
by Roy Johnson (Monsignor) on Mar 05, 2004 at 21:51 UTC
    You don't have to declare the variable where you assign it. You can declare it above, so that each block is using the same $response, and it's available outside them:
    die "Required argument missing!\n" unless ($ARGV[0]); my $response; if ($ARGV[0] =~ /Hello/i) { $response = "Hello to you.\n"; }else{ $response = "Huh?\n"; } print $response;
    If by "global" you mean a lexical outside of any enclosing block (which is not the customary meaning), and you have some aversion to having such a thing, just wrap a block around the area where it's used:
    die "Required argument missing!\n" unless ($ARGV[0]); { #scoping block my $response; if ($ARGV[0] =~ /Hello/i) { $response = "Hello to you.\n"; }else{ $response = "Huh?\n"; } print $response; }

    The PerlMonk tr/// Advocate
      Yeah I have used the incomplete type declerations (my $response;) for this in the past which to me was the best way but it was still used in a global sense.

      I was just wanting to know if there was a better way that was used or could be used. I usualy just declare all my global variables together first. my($response, $var2, $var3, $var4); etc....

      So it would usually look like such in one of my scripts:

      #!/usr/bin/perl -w use strict; my($response); die "Required argument missing!\n" unless ($ARGV[0]); if ($ARGV[0] =~ /Hello/i) { $response = "Hello to you.\n"; }else{ $response = "Huh?\n"; } print $response;

      This interest was sparked because in other language I code in I usually try to "limit the scope" of my variables and try not to use global variables too much. In some languages this is looked down upon and can cause problems.

        My 2nd example showed how you can limit the scope of the lexical variable: just wrap the desired area in a block. The variable will exist only within that block. That is the "better way" you were looking for.

        You should note that your use of "global" is not the same as Perl's. Variables that are declared with my are never globals. They are lexicals, scoped either by an enclosing block, or by the file if there is no enclosing block. Globals, on the other hand, are associated with packages and have entries in symbol tables. It is this type of global that perl was warning you about when it complained about your print $response statement. Because you had not declared the variable as lexical in the enclosing scope, $response was presumed to be $main::response (that is, the global variable named $response within the main:: package).


        The PerlMonk tr/// Advocate

        Here's an option that uses Roy's scoping block and your possibly lengthy list of global variables.

        You create a hash reference inside the scoping block: it is not visible to any code outside the block including any subroutines (one of the main problems with globals). You then let Perl autovivify your $response, $var2, etc. as keys in the hashref: all of these will go out-of-scope with the hashref (sorry, that's probably obvious).

        You can pass the hashref to subroutines that need any of the global variables: that's good for (a) performance because you're only passing a single scalar and (b) good for maintenance because you don't need to align lengthy lists of arguments between caller and callee. Furthermore, because it's a reference you don't need to return it.

        The only cautionary note I'd raise is don't trap the hashref in a closure: that will have a high probability of causing you grief.

        Here's a fragmented example:

        #!/usr/bin/perl ... { # scoping block my $rh_vars = {}; if (...) { $rh_vars->{response} = 'whatever'; if (...) { $rh_vars->{var2} = 'something'; } func($rh_vars); } print $rh_vars->{response}; } # $rh_vars out-of-scope here ... exit 0; sub func { my $rh_vars = shift; if (exists $rh_vars->{var2}) { # do something with "var2" } }

        I've kept this example code short and skeletal. I'll happily expand or clarify any points if necessary.

        As an aside, you may notice that this code is halfway down the road to being object-oriented: you've encapsulated and hidden your data in the hashref which is acting like a light-weight object (unblessed - no inheritance).

        PN5

Re: Best way to pass variables?
by Corion (Patriarch) on Mar 05, 2004 at 21:51 UTC

    You can't avoid having a variable as global if it is used in global scope. The first use does not necessarily imply the scope of a variable, as you found out.

    I would possibly rewrite your code as follows:

    sub get_response { my ($res) = @_; die "Required argument missing" unless $res; if ($res =~ /Hello/i) { return "Hello to you.\n"; } else { return "Huh?\n"; }; }; print get_response(@ARGV);
Re: Best way to pass variables?
by PodMaster (Abbot) on Mar 05, 2004 at 21:52 UTC
    use strict; my $fooberry = " hello "; die "arg arg arg" unless @ARGV; { { $fooberry = "arg" if $ARGV[0] =~ /arg/; } } print $fooberry;

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

Re: Best way to pass variables?
by BUU (Prior) on Mar 06, 2004 at 07:51 UTC
    While this only works for 'binary' cases, I like: $response = $ARGV[0]=~/foo/?'hello':'huh'; And you can nest ternaries or use if modifiers.