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

I came across some code that I'm trying to understand. It involves the symbol table. I read about the symbol table in a post Of Symbol Tables and Globs, but I have questions I couldn't find the answers to there.

use Data::Dumper; $hash = { 'user1' => { 'oldpassword' => 0, 'filesize' => '14360', 'logins' => 1 }, 'user2' => { 'oldpassword' => 0, 'filesize' => '1220', 'logins' => 15 }, 'user3' => { 'oldpassword' => 1, 'filesize' => '1780', 'logins' => 7 } }; sub KeysByLogins { my $hash = shift; map { $_->[1] } sort { $a->[0] <=> $b->[0] } map { [ $hash{$_}->{logins}, $_ ] } keys %$hash; } our @keys = KeysByLogins($hash); foreach my $key (@keys) { print Data::Dumper->Dump([$hash->{$key}], [$key]) . "\n"; # number 1 #print Data::Dumper->Dump([$hash{$key}], [$key]) . "\n"; # number + 2 } print $::{hash}. ' ', $::{keys};
The output was:
$user1 = { 'filesize' => '14360', 'oldpassword' => 0, 'logins' => 1 }; $user3 = { 'filesize' => '1780', 'oldpassword' => 1, 'logins' => 7 }; $user2 = { 'filesize' => '1220', 'oldpassword' => 0, 'logins' => 15 }; *main::hash *main::keys
The line inside the sub map { [ $hash{$_}->{logins}, $_ ] } keys %$hash; isn't using the reference notation $hash->{logins} but is referring to the hash %hash, which wasn't declared. I'm guessing it springs up from the glob(?) in the symbol table (for the duration of the sub).

Then, later on in the print loop, the hash ref is used in line no. 1.

If this line is commented out and line 2 is uncommented, Dumper shows the hash as empty (when accessing the undeclared %hash variable).

Would that be because %hash only springs up in the scope of the subroutine, then is inaccessible outside the sub (in the print routine).

I guess I don't understand the workings of the symbol table. Thanks for any clear explanation of this code snippet.

Replies are listed 'Best First'.
Re: Question about the symbol table
by Anonymous Monk on Feb 26, 2016 at 16:12 UTC

    This code was obviously written without strict and should therefore only serve as an example as to why strict should always be used. Anyways, on to the analysis:

    The code is obviously supposed to sort its output by number of logins, but if you run it several times, you'll see it doesn't work!

    First clue as to the error: If you turn on warnings and strict - as one always should, even when trying to understand legacy code! - and then declare our ($hash, %hash); at the top of the code, you'll see warnings like this: "Use of uninitialized value in numeric comparison (<=>) at ..."

    sub KeysByLogins is the only place where %hash is referenced, and there it is only read from, not written to, that's why it remains empty.

    If you break the Schwartzian Transform happening in that sub into pieces, you can see that happening:

    sub KeysByLogins { my $hash = shift; my @temp = map { [ $hash{$_}->{logins}, $_ ] } keys %$hash; print Dumper(\@temp); return map { $_->[1] } sort { $a->[0] <=> $b->[0] } @temp; }

    The first element in each pair is always undef, because %hash is empty.

    So it's really not got much to do with the symbol table, just a single typo bug caused by not using strict.

      P.S. To directly answer your question (and make a minor correction*)

      Would that be because %hash only springs up in the scope of the subroutine,

      No, undeclared variables (when strict vars is not in use) are package variables (global), see e.g. our. In this case, there are two, the scalar $hash which holds a reference to an anonymous hash (the data structure being used in this code), and %hash, a hash which is only read from in one place in the code, which is why it remains almost empty*.

      * The dereference operation actually autovivifies some empty anonymous hashes:

      print Dumper(\%hash); __END__ $VAR1 = { 'user2' => {}, 'user3' => {}, 'user1' => {} };
Re: Question about the symbol table
by Cristoforo (Curate) on Feb 26, 2016 at 17:54 UTC
    Thank you for your analysis. Indeed, when I added some more elements to the anonymous hash, the sort failed. Your modified Schwartzian Transform did show the undef hash vals.

    Thanks again. It explained what was going on.