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

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

I was recently tasked with 'porting' a large body of code from Perl 5.6 to 5.8.x. The following construct (simplified), which was legal in 5.6 is no longer so:

my %$h = ('name' => 'value', ...); ... print $h->{'name'};
I suspect the original author of the sin of cut-and-paste and not understanding references, and hash ref initializers. I changed the code to:
my %h = ('name' => 'value', ...); ... print $h{'name'};
and all is well now. But when explaining this to the group, I couldn't answer the question of whether my change was any better than:
my $h = {'name' => 'value', ...}; ... print $h->{'name'};
So my question is whether anyone has a good reason to choose one over the other? My assumption is that there is no difference.



pbeckingham - typist, perishable vertebrate.

Replies are listed 'Best First'.
Re: Relative Merits of References
by ikegami (Patriarch) on May 11, 2006 at 14:33 UTC
    I see some differences.
    • my $h = {'name' => 'value', ...};
      creates two variables (a hash and a reference to it), while
      my %h = ('name' => 'value', ...);
      creates only the hash.

    • my $h = {'name' => 'value', ...};
      requires dereferencing to access the hash, while you might need to create references to
      my %h = ('name' => 'value', ...);
      to use it.

    • It is less obvious in
      my $h = {'name' => 'value', ...};
      than in
      my %h = ('name' => 'value', ...);
      that was are dealing with a hash because the hash sigil is not used in the former.

    • However, all of the above are rather inconsequential. The real difference is that it would have been much less work to switch to
      my $h = {'name' => 'value', ...};
      rather than to
      my %h = ('name' => 'value', ...);
      since the rest of the code was expecting a reference to a hash.

Re: Relative Merits of References
by Hue-Bond (Priest) on May 11, 2006 at 13:51 UTC

    References are useful to pass data to functions. Using references, you pass a single scalar to the function, so perl doesn't have to copy the entire thing. Update: And they are also useful to create complex data structures. A hash of hashes is really a hash of references to hashes.

    --
    David Serrano

      Indeed references are just great! OTOH I have also seen very bad examples of programs abusing references in every place. I remeber this guy who kept passing stuff around referencing and dereferencing all the time, and in fact at one point he got stuck! Had he used @arrays and %hashes in the first place, is code would have been much KISSer.

      Thanks David

      Understood, when passing by reference is needed, having a reference around is handy, but then creating one using

      \%h
      is not exactly complicated. I'm wondering if there are any reasons to choose one over the other - this example doesn't seem to qualify.



      pbeckingham - typist, perishable vertebrate.
Re: Relative Merits of References
by blazar (Canon) on May 11, 2006 at 14:03 UTC
    I was recently tasked with 'porting' a large body of code from Perl 5.6 to 5.8.x. The following construct (simplified), which was legal in 5.6 is no longer so:
    my %$h = ('name' => 'value', ...); ... print $h->{'name'};

    Was it legal? I can understand my %$h to work as a symref. But then why should $h->{'name'} be correct? Or else does this mean that

    my %$h = ('name' => 'value', ...); # was "expanded" to my $h = {'name' => 'value', ...};

    or something like that? If so, then I agree on such behaviour to have been removed, as you declare variables, not variable dereferences that autovivify the reference. That would be too much of a dwimmery, doing what in fact I shouldn't mean.

Re: Relative Merits of References
by Zaxo (Archbishop) on May 11, 2006 at 14:20 UTC

    I think that the alternative is better than yours for a pretty simple reason. With it, you don't need to find and edit out the dereference op for every use of the hash. Only one line of code needs to change.

    context context -my %$h = ('name' => 'value', ...); +my $h = {'name' => 'value', ...}; context print $h->{'name'};

    After Compline,
    Zaxo

Re: Relative Merits of References
by YuckFoo (Abbot) on May 11, 2006 at 19:50 UTC
    Since almost all hashes and arrays I make end up getting passed to subroutines, I usually just create them as refs. This has the (slight) benefit having a consistent point of dereference in mainline code and subroutine code instead of having to remember to move the arrow around. Also foo($this) looks nicer that foo(\%that).

    RefFoo

    #!/usr/bin/perl use strict; my %rgbs = ( yellow => { r => 0xff, b => 0, g => 0xff, }, magenta => { r => 0xff, b => 0xff, g => 0, }, ); print "$rgbs{yellow}->{r}\n"; foo(\%rgbs); sub foo { my $rgbs = shift; print "$rgbs->{yellow}{r}\n"; }
Re: Relative Merits of References
by wfsp (Abbot) on May 11, 2006 at 14:17 UTC
    It would make a difference if you wanted to pass two hashes to a sub.

    #!/usr/bin/perl use strict; use warnings; my $h1 = { one => 1, two => 2, }; my $h2 = { three => 3, four => 4, }; my_sub($h1, $h2); sub my_sub { my ($h1, $h2) = @_; print "$_ $h1->{$_}\n" for keys %{$h1}; print "$_ $h2->{$_}\n" for keys %{$h2}; }
    In these circumstances refs are, imo, the only way to go. :-)

      Or alternatively:

      #!/usr/bin/perl use strict; use warnings; my %h1 = ( one => 1, two => 2, ); my %h2 = ( three => 3, four => 4, ); my_sub(\%h1, \%h2); sub my_sub { my ($ref1, $ref2) = @_; my %h1 = %{$ref1}; my %h2 = %{$ref2}; # Or "my %h1 = %{(shift)};" will work too, but I don't really like it. print "$_ $h1{$_}\n" for keys %h1; print "$_ $h2{$_}\n" for keys %h2; }

      if you want to keep the hash. This is really the only way to go :)

        Now you're making an expensive copy of a hash. You could just be using the references with:

        my ($r1, $r2) = @_; ... print "$_ $h1{$_}\n" for keys %$r1; print "$_ $h2{$_}\n" for keys %$r2;

        Update:Thanks again David, yes, I meant:

        print "$_ $r1->{$_}\n" for keys %$r1; print "$_ $r2->{$_}\n" for keys %$r2;



        pbeckingham - typist, perishable vertebrate.
Re: Relative Merits of References
by johngg (Canon) on May 11, 2006 at 14:39 UTC
    I think that construct is still legal in Perl 5.8.x but you may be falling foul of use strict;. I may be wrong but I suspect that your $h contains a string which is the name of another variable thus setting up a soft reference. The following have been run under Perl 5.8.4

    Firstly with no use strict; or use warnings;

    #use strict; #use warnings; $h = "fred"; %$h = (name => "Jim", age => 34); print $h->{name}, "\n"; print $fred{age}, "\n";

    produces

    Jim 34

    Switching on strict like this

    use strict; use warnings; our %fred; my $h = "fred"; %$h = (name => "Jim", age => 34); print $h->{name}, "\n"; print $fred{age}, "\n";

    produces

    Can't use string ("fred") as a HASH ref while "strict refs" in use at +pbeck2 line 8.

    because use strict; objects to soft references. Only by switching strict off for the duration of a code block can you use soft references.

    use strict; use warnings; our %fred; my $h = "fred"; { no strict q(refs); %$h = (name => "Jim", age => 34); print $h->{name}, "\n"; } print $fred{age}, "\n";

    produces

    Jim 34

    I hope this clarifies what might be happening with your "illegal" code.

    Cheers,

    JohnGG

Re: Relative Merits of References
by clinton (Priest) on May 11, 2006 at 14:19 UTC
    Why do you think that that construct is illegal? I tried this code in perl 5.8.7 and it works without any warnings.

    use strict; use warnings; use Data::Dumper; my $a; %$a=(a=>1,b=>2); print Dumper($a);

    And it prints

    $VAR1 = { 'a' => 1, 'b' => 2 };

    UPDATE : I see now that the illegal part was the "my %$h" part rather than the assigment part. So that could have been changed simply by splitting the declaration to a separate line, or by changing the list to a {}.

    However it seems an odd way to do it when you could just say :

    $a={a=>1,b=>2};

    I benchmarked the two versions, and the first %$a=() is 50% slower than the second ($a={}).

    Regarding the difference between $h{name} versus $h->{name}, there is a slight speed difference (10%), because the second version first needs to dereference $h before doing the lookup, but it is so blazingly fast that I wouldn't worry about it. (Tested on a small hash - not sure for bigger hashes)

    Rather just use whatever is easier to read, as there are bound to be other bottlenecks that make much more difference to your code's performance.

      You can't change the code, show that the changed code is legal and claim the original was legal.

      The OP has
      my %$a=(a=>1,b=>2);
      You have
      my %a; %$a=(a=>1,b=>2);

        Yep, I missed the significance of the my %$h = .... I'm thinking back now to dimly remembered exam advice from more than thirty years ago, "Read the question carefully before answering." I'll try to pause for thought next time.

        Cheers,

        JohnGG

      %$a={a=>1, b=>2};

      I would need to check but it looks like it does the assignment of the hash in $a and then copies it into a hash (%) which is then thrown away as it is unassigned.

      I don't have a copy of 5.6 available but it would be interesting to see if my %$a assigned both %a and $a via the glob. In 5.8 it appears to do as suggested earlier.

      UnderMine

Re: Relative Merits of References
by zerogeek (Monk) on May 12, 2006 at 07:56 UTC
    I just wanted to thank all of you, especially pbeckingham, for nodes (did I use that term correctly?) like this one.

    Being new to Perl - I'm only up to Ch13 in the Llama book - these are the types of posts that are really helpful to read through. It is very interesting and helpful to see all the different solutions to the same problem.

    Additionally, the comments regarding WHY you chose one way over another are useful.

    Again-
    Keep 'em coming. I'm feeling smarter every day!

      If you're going to be maintaining older code, I recommend Perl Medic be on your reading list, as well.
Keep it consistent
by kbrint (Sexton) on May 12, 2006 at 12:48 UTC
    If I need to pass it by reference to many places, then I create it as a reference as well. That way, code in the scope where it's created looks the same as elsewhere; both can use $h->{key}. Otherwise, in the scope where it's created you use $h{key}, and everywhere else you use $h->{key}