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

Hi Everyone, I am currently reading up on the Camel I am unable to wrap my head around the following piece of code when paging through the chapter on Packages:
1 #!/usr/bin/perl 2 3 use strict; 4 use warnings; 5 6 7 sub fillerup ($); 8 9 my %fillme = (gas_pr => 3.59, gas_capacity => 14); 10 11 12 local $\ = "\n"; 13 14 print '-'x80; 15 foreach my $key (keys %fillme ) { 16 print $key . '=' . $fillme{$key}; 17 } 18 19 fillerup ( \%fillme ); 20 21 print '-'x80; 22 23 foreach my $key (keys %fillme ) { 24 print $key . '=' . $fillme{$key}; 25 } 26 27 sub fillerup ($) { 28 no strict; 29 local *hashref = shift; 30 # my $hashref = shift; 31 32 print "Prior to inserting a record in fillerup..."; 33 print '*'x80; 34 35 foreach my $key (keys %$hashref ) { 36 print $key . '=' . $hashref->{$key}; 37 } 38 39 $hashref->{filled_up} = 12.3; 40 41 print '*'x80; 42 43 foreach my $key (keys %$hashref ) { 44 print $key . '=' . $hashref->{$key}; 45 } 46 47 print '*'x80; 48 49 return; 50 }
Now in the subroutine fillerup, I expect the auto-vivification of a new hash. However, the output is as follows:
------------------------------------------------------------- gas_capacity=14 gas_pr=3.59 Prior to inserting a record in fillerup... ********************************************************************** +******** ********************************************************************** +******** filled_up=12.3 ********************************************************************** +******** ------------------------------------------------------------- gas_capacity=14 gas_pr=3.59
* Note that the output has been reformatted to promote visibility. (1) Not sure why the locally created glob does not get the reference to the fillme hash. It does get a reference but one to an anonymous hash. If I comment out line 29 and uncomment line 30, I get the following output:
------------------------------------------------------------- gas_capacity=14 gas_pr=3.59 Prior to inserting a record in fillerup... ********************************************************************** +******** gas_capacity=14 gas_pr=3.59 ********************************************************************** +******** gas_capacity=14 filled_up=12.3 gas_pr=3.59 ********************************************************************** +******** ------------------------------------------------------------- gas_capacity=14 filled_up=12.3 gas_pr=3.59
* Note that the output has been reformatted to promote visibility. (2) Also, the program fails to run when the  local qualifier on line 29 is changed to  our. I have read through the tutorial on how local scoping works and I thought I was clear but apparently not. I would appreciate your time and help if you could aid me in my understanding of the interaction of scoping and globs. Thanks, - Markov

Replies are listed 'Best First'.
Re: Variable scoping and globs and reference passing to subroutines!!!
by ig (Vicar) on Dec 19, 2008 at 02:20 UTC

    The misunderstanding may be regarding what happens when you assign a hash ref to a glob rather than what local is doing.

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; sub fillerup ($); my %fillme = (gas_pr => 3.59, gas_capacity => 14); local $\ = "\n"; print '-'x80; foreach my $key (keys %fillme ) { print $key . '=' . $fillme{$key}; } fillerup ( \%fillme ); print '-'x80; foreach my $key (keys %fillme ) { print $key . '=' . $fillme{$key}; } sub fillerup ($) { no strict; local *hashref = shift; # my $hashref = shift; print "Prior to inserting a record in fillerup..."; print '*'x80; foreach my $key (keys %$hashref ) { print $key . '=' . $hashref->{$key}; } $hashref->{filled_up} = 12.3; print '*'x80; foreach my $key (keys %$hashref ) { print $key . '=' . $hashref->{$key}; } print '*'x80; print "\*hashref: " . Dumper(*hashref); print "\$hashref: " . Dumper($hashref); print "\%\$hashref: " . Dumper(%$hashref); print "\\\%\$hashref: " . Dumper(\%$hashref); print "\\\%hashref: " . Dumper(\%hashref); print "\\\%fillme: " . Dumper(\%fillme); return; }

    Produces

    ---------------------------------------------------------------------- +---------- gas_capacity=14 gas_pr=3.59 Prior to inserting a record in fillerup... ********************************************************************** +********** ********************************************************************** +********** filled_up=12.3 ********************************************************************** +********** *hashref: $VAR1 = *::hashref; $hashref: $VAR1 = { 'filled_up' => '12.3' }; %$hashref: $VAR1 = 'filled_up'; $VAR2 = '12.3'; \%$hashref: $VAR1 = { 'filled_up' => '12.3' }; \%hashref: $VAR1 = { 'gas_capacity' => 14, 'gas_pr' => '3.59' }; \%fillme: $VAR1 = { 'gas_capacity' => 14, 'gas_pr' => '3.59' }; ---------------------------------------------------------------------- +---------- gas_capacity=14 gas_pr=3.59

    Update: See Typeglobs and Filehandles for more info on assignment to typeglobs. Note that assigning the reference to your global hash to your localized typeglob sets %hashref to be an alias to %fillme and does not set $hashref to a reference to the hash as you are expecting. You then autovivify $hashref with a reference to a new hash containing only the key 'filled_up', and leaving %hashref unchanged.

Re: Variable scoping and globs and reference passing to subroutines!!!
by tilly (Archbishop) on Dec 19, 2008 at 06:33 UTC
    As was pointed out, your confusion was because assigning a hashref to *foo assigns the hash to %foo.

    But the reason that I am responding is to head off a bad habit. Lose the prototypes. They don't do what you think and you almost definitely don't want what they actually do. Prototypes in Perl do argument coercion. They don't prevent any potential mistakes or warn you of anything wrong. They just provide the possibility of minor convenience at the risk of causing serious confusion and introducing unexpected bugs.

      Awesome. Thank you Sir / Madame for all your help and time.
      Ran it thru my debugger and it looks such.

      Lessons learnt:
        > Will use Data::Dumper for debugging from now on.
        > As to not using subroutine declarations, should I then disable the  use strict 'subs';? I understand its an error to use subroutine_name when use strict 'subs' is in effect and the subroutine is not pre-declared. (You could use subroutine_name() though.) I just checked up Perl::Tidy and you are indeed correct that no subroutine declarations are done - subroutines are directly defined. Also in another of the perl scripts used by perl-support, the author ends up defining all the subroutines prior to using them. Would you encourage such authoring to be best-practice?
        You misunderstand. I was advising you to not use the prototype. That is you should write:
        sub foo { # do something }
        rather than
        sub foo ($) { # do something }
        But since you have raised the issue of predeclaration, the usual practice is to pre-declare nothing and to call them with parens. For instance that is what Damian Conway recommends in Perl Best Practices. Incidentally it isn't just strict that doesn't like the bare form if no declaration has happened. If foo hasn't been declared then foo "bar"; is a syntax error. So no matter what you do, there is no reason to avoid strict.