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

Hi monks,

I came across the code below in the perl man page under 'perlreftut'.
# Assume the following input delimited be a newline Chicago, USA Frankfurt, Germany Berlin, Germany Washington, USA Helsinki, Finland New York, USA 1 while (<>) { 2 chomp; 3 my ($city, $country) = split /, /; 4 push @{$table{$country}}, $city; 5 } 6 7 foreach $country (sort keys %table) { 8 print "$country: "; 9 my @cities = @{$table{$country}}; 10 print join ’, ’, sort @cities; 11 print ".\n"; 12 } In the first part, line 4 is the important one. We’re going to have a + hash, %table, whose keys are country names, and whose values are (re +ferences to) arrays of city names. After acquiring a city and countr +y name, the program looks up $table{$country}, which holds (a referen +ce to) the list of cities seen in that country so far.
While I understand more or less what's going on, I don't particularly understand line 4, despite the author's explanation. How is %table created? I thought that for a hash to be created, a value must be assigned to the key, as in:

$hash{$key} = $value;

How are the values assigned to the keys in %table in the above code?

Hope someone can enlighten me :)

cheers,

kiat

Replies are listed 'Best First'.
Re: Question on reference
by BrowserUk (Patriarch) on Oct 26, 2002 at 15:37 UTC

    In addition to what diotilevi correctly said regarding autovivication, the sample code is a slightly ecclectic mix of 'old' and 'new' Perl in that it mixes the use of selected my'd variables with global vars.

    Cleaned up to bring it in line with more modern usage and making it use strict; complient it would look something like this.

    Note that the code within =pod/=cut lines is simply comments showing what code would be requireed were it not for autovivication.

    #! perl -sw use strict; # Without the next %table referes to a global var which doesn't declar +ation. my %table; while (<>) { chomp; my ($city, $country) = split /, /; push @{$table{$country}}, $city; # The line above is equivalent to =pod if (not exists $title{$country}) { my @array; push @array, $city; $title{$country} = \@array; } else { my $arrayRef = $title{$country}; push @{$arrayRef}, $city; } =cut } foreach my $country (sort keys %table) { print "$country: "; my @cities = @{$table{$country}}; print join ',', sort @cities; print ".\n"; #The 3 lines above could be replaced by =pod print join( ',' @{$table{$country}} ), "\n"; =cut }

    Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!
      You have to put POD directives in a paragraph of their own, ie preceeded and followed by a blank line.

      Makeshifts last the longest.

        No you don't.

        Not if you are only using them as a convenient form of multi line comment. I seriously doubt that anyone is going to run perldoc on this snippet.


        Nah! Your thinking of Simon Templar, originally played by Roger Moore and later by Ian Ogilvy
      Thanks, BrowserUk! Your expanded code is a great help!
Re: Question on reference
by diotalevi (Canon) on Oct 26, 2002 at 14:48 UTC

    You've just noticed autovivification. In this case the @{$table{$country}} assumes that the value of $table{$country} must be an array reference and so perl is helpful and just creates it if it doesn't already exists. It's that wrapping @{} that's doing the missing bit for you.

    __SIG__ use B; printf "You are here %08x\n", unpack "L!", unpack "P4", pack "L!", B::svref_2object(sub{})->OUTSIDE;