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

Why are the below hash values not equal ?

use feature 'say'; my $hash; $hash{key1}{key2} = 'val1'; $hash->{key1}->{key2} = 'val2'; say $hash{key1}{key2}; say $hash->{key1}->{key2}; =comment Expected: val2 val2 Actual: val1 val2 =cut

Replies are listed 'Best First'.
Re: Hash syntax
by hippo (Archbishop) on Aug 19, 2024 at 08:09 UTC

    Short answer: because you have not used strict like you should have done.

    Long answer: One is a hash and the other is a hash reference. They are two different variables, the fact that your code has chosen to give them the same name is irrelevant (and obviously confusing), so don't do that unless you have a very good reason for doing so.


    🦛

Re: Hash syntax
by Discipulus (Canon) on Aug 19, 2024 at 08:03 UTC
    Hello mvanle and a late welcome to the monastery,

    > Why are the below hash values not equal ?

    because you missed to

    use strict; use warnings;

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Hash syntax
by LanX (Saint) on Aug 19, 2024 at 11:06 UTC
    Hi,

    welcome to the monastery, :)

    That's because Perl has two forms for each variable type, and you are implicitly using both for hashes:

    • %hash is a classic hash with explicit sigil % (what I call the list form)
    • $hash is in your case a hash reference

    Using strict would have warned you that you didn't declare two different variables %hash and $hash beforehand.

    See perlreftut *

    Background for this "dualism" is that references were only introduced with Perl5 and the old Perl4 syntax was kept compatible.

    Furthermore does Perl need context, the $ upfront denotes scalar context.*

    Edit

    Demo in the debugger

    ~ $ perl -de0 ... DB<4> $hash{key1} = 'val1' # add scalar to %hash DB<5> say %hash # list form key1val1 DB<6> x \%hash # dump 0 HASH(0xb400007063b64e70) 'key1' => 'val1' DB<7> use strict; $hash{key1} = 'val1' Variable "%hash" is not imported at... # versa DB<8> $hash->{key2} = 'val2' # add scalar to $hash DB<9> say $hash # hash ref HASH(0xb400007063ceb198) DB<10> x $hash # dump 0 HASH(0xb400007063ceb198) 'key2' => 'val2' DB<11> use strict; $hash->{key2} = 'val2' Variable "$hash" is not imported at

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

    *) updated

      Hello Monks,

      The several answers are spot-on, including encouraging the writer to use the debugger, but I have a curious issue that is somewhat related, to wit: for my amusement I've written three great circle calculators (Spherical Law of Cosines, Haversine and Vincenty Inverse; C, Perl and Python versions) that stores airport locations and their geodesic parameters (latitude and longitude) in a Btree BerkeleyDB. The hash instances are tied with MLDBM. I load them into the DB from a tab-delimited file. Here is my airport hash definition:

      $APref->{++$id} = { APname => $loc, geodesics => [ $lat, $long, dms2rad($lat), dms2rad($long) ] };

      $APref is a hash reference. The key is an interger rather than a location string so I can say, for example; calculate the GC distance between 17 and 33. When I access that array, as in when printing out a list or passing two airport instances to my GC calculators I would like to say:

      $APref->{$key}{geodesics}->[0..3]

      but this causes Perl (5.38.2) to barf. This expression

      @{$APref->{$key}{geodesics}}[0..3]

      is the only way to fetch that slice. My standard rule is to use the arrow operator wherever there is a reference in such an expression, but the block indirection version is the only syntax that works, in this case. Is their something about taking an array slice that defeats the arrow operator, or am I missing something? I have no problem using or understanding that noisier indirection syntax, but there must be an explanation beyond my current understanding of the arrow operator. Thoughts please?

      U P D A T E

      Thank you LanX and tybalt89. You're never too old to learn something you didn't know about Perl.

      ->@[0..3]

      is exactly what I was looking for, and context is everything; a slice is not a scalar.

        $APref->{$key}{geodesics}->@[0..3]

        Close enough?

        This should be a proper thread, I considered that it's moved into one.

        > I have no problem using or understanding that noisier indirection syntax, but there must be an explanation beyond my current understanding of the arrow operator. Thoughts please?

        Short answer

        Symmetry!

        $arr_ref->[@slice] doesn't "work" because the analog $arr[@slice] also doesn't do what you mean.

        NB: @arr[@slice] is different.

        Long answer

        Perl is pretty consistent that $ means scalar and @ means a list. The whole context rules depend on it. Something like $ref->[@slice] is as misleading as $arr[@slice] because a scalar has to be returned. (What's happening here is that the scalar value of @slice is calculated instead of a list)

        For a slice the correct syntax is @arr[@slice]

        Compare the analogy

        DB<12> @a = "a".."z"; $ar = \@a DB<13> p $ar->[1], $a[1] bb DB<14> @slice = 1..3 DB<15> p $ar->[@slice], $a[@slice] dd DB<16> p $ar->@[@slice], @a[@slice] bcdbcd DB<17> p @$ar[@slice], @a[@slice] bcdbcd

        The deref syntax in line 16 is newer and was introduced to avoid @{...} constructs like in line 17 (In this case the brackets are optional)

        What you are suggesting would break the symmetry and cause confusion and bugs.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

        Updates

        Added short answer

Re: Hash syntax
by perlfan (Parson) on Aug 24, 2024 at 00:41 UTC
    $hash and %hash are 2 completely different variables. You can have a @hash that is an array if you really wanted to go nuts :-) ...

    Look at the difference that dereferencing with the first -> makes:

    use strict; use warnings; use feature 'say'; my $hash_ref = {}; # SCALAR pointing to an empty HASH reference my %hash = (); # actual HASH $hash{key1}{key2} = 'val1'; $hash_ref->{key1}->{key2} = 'val2'; $hash_ref = { # HASH reference, note: curly brace *** key1 => { # key1 is a nested HASH reference key2 => 'val2', # key2 is pointing to the value "val2" }, }; %hash = ( # actual HASH, note: parens *** key1 => { # key1 is a nested HASH reference key2 => 'val1', # key2 is pointing to the value "val2" }, ); # accessing "key2" in $hash_ref: say $hash_ref->{key1}->{key2}; # the first "->" is important say $hash_ref->{key1}{key2}; # accessing "key2" in %hash: say $hash{key1}{key2}; # lack of the first "->" is important say $hash{key1}->{key2}; __END__
    Output:
    val2 val2 val1 val1
    To underscore the difference also, you can look at how an a HASH can be assigned with an even sized list,
    my @hash = ("key3", "val3", "key4", "val4"); # actual ARRAY my %hash3 = @hash; # even sized (2, 4, etc) list assignment works say $hash3{key3}; say $hash3{key4};
    Output:
    val3 val4