Hashes

Hash Element Access

To access an element of a hash, use syntax that looks like this:
$hash{$some_key}
This is similar to what we used for array access, but here we use curly braces instead of square brackets around the subscript(key). And that key expression is now a string, rather than a number:

$family_name{"fred"} = "flintstone";
$family_name{"barney"} = "rubble";

foreach $person (qw< barney fred>) {
    print "I've heard of $person $family_name{$person}.\n";
}

The name of the hash is like any other Perl identifier (letters, digits, and underscores, but can’t start with a digit). And it’s from a separate namespace; that is, there’s no connection between the hash element $family_name{"fred"} and a subroutine &family_name, for example. Of course, there’s no reason to confuse everyone by giving everything the same name.

When you store something into an existing hash element, it overwrites the previous value:

$family_name{"fred"} = "astaire"; # gives new value to existing element 
$bedrock = $family_name{"fred"}; # gets "astaire"; old value is lost

That’s analogous to what happens with arrays and scalars; if you store something new into $pebbles[17] or $dino, the old value is replaced. If you store something new into $family_name{"fred"}, the old value is replaced as well.

Hash elements will spring into existence by assignment:

$family_name{"wilma"} = "flintstone"; # adds a new key (and value) 
$family_name{"betty"} .= $family_name{"barney"}; # creates the element if needed

And accessing outside the hash gives undef:

$granite = $family_name{"larry"}; # No larry here: undef
The Hash As a Whole

To refer to the entire hash, use the percent sign (%) as a prefix. So, the hash we’ve been using for the last few pages is actually called %family_name.

For convenience, a hash may be converted into a list, and back again. Assigning to a hash is a list-context assignment, where the list is made of key-value pairs:

%some_hash = ("foo", 35, "bar", 12.4, 2.5, "hello", "wilma", 1.72e30, "betty", "bye\n");

The value of the hash (in a list context) is a simple list of key-value pairs:

@any_array = %some_hash;

We call this unwinding the hash—turning it back into a list of key-value pairs. Of course, the pairs won’t necessarily be in the same order as the original list:

print "@any_array\n";
# gives result (on my machine): 2.5 hello bar 12.4 wilma 1.72e+30 foo 35 betty bye \n
Hash Assignment

It’s rare to do so, but a hash may be copied using the obvious syntax: %new_hash = %old_hash;

This is actually more work for Perl than meets the eye. Unlike what happens in lan- guages like Pascal or C, where such an operation would be a simple matter of copying a block of memory, Perl’s data structures are more complex. So, that line of code tells Perl to unwind the %old_hash into a list of key-value pairs, then assign those to %new_hash, building it up one key-value pair at a time.

It’s more common to transform the hash in some way, though. For example, we could make an inverse hash:
%inverse_hash = reverse %any_hash;

This takes %any_hash and unwinds it into a list of key-value pairs, making a list like (key, value, key, value, key, value, ...). Then reverse turns that list end-for-end, making a list like (value, key, value, key, value, key, ...). Now the keys are where the values used to be, and the values are where the keys used to be. When that’s stored into %inverse_hash, we’ll be able to look up a string that was a value in %any_hash—it’s now a key of %inverse_hash.

The Big Arrow

That is, in the Perl grammar, any time that you need a comma ( , ), you can use the big arrow instead; it’s all the same to Perl. So here’s another way to set up the hash of last names:

my %last_name = ( # a hash may be a lexical variable 
    "fred" => "flintstone", 
    "dino" => undef, 
    "barney" => "rubble", 
    "betty" => "rubble", 
);

Here, it’s easy (or perhaps at least easier) to see whose name pairs with which value, even if we end up putting many pairs on one line. And notice that there’s an extra comma at the end of the list. As we saw earlier, this is harmless, but convenient; if we need to add additional people to this hash, we’ll simply make sure that each line has a key-value pair and a trailing comma. Perl will see that there is a comma between each item and the next, and one extra (harmless) comma at the end of the list.

Hash Functions

The keys and values Functions

The keys function yields a list of all the keys in a hash, while the values function gives the corresponding values. If there are no elements to the hash, then either function returns an empty list:

my %hash = ("a" => 1, "b" => 2, "c" => 3); 
my @k = keys %hash; 
my @v = values %hash;

In a scalar context, these functions give the number of elements (key-value pairs) in the hash. They do this quite efficiently, without having to visit each element of the hash:

my $count = keys %hash; # gets 3, meaning three key-value pairs

Once in a long while, you’ll see that someone has used a hash as a Boolean (true/false) expression, something like this:

if (%hash) { 
    print "That was a true value!\n"; 
}

That will be true if (and only if) the hash has at least one key-value pair. So, it’s just saying, “If the hash is not empty...” But this is a pretty rare construct, as such things go.

The each Function

If you wish to iterate over (that is, examine every element of) an entire hash, one of the usual ways is to use the each function, which returns a key-value pair as a two-element list. On each evaluation of this function for the same hash, the next successive key-value pair is returned, until all the elements have been accessed. When there are no more pairs, each returns an empty list.

In practice, the only way to use each is in a while loop, something like this:

while ( ($key, $value) = each %hash ) { 
    print "$key => $value\n"; 
}

Note:
First, each %hash returns a key-value pair from the hash, as a two-element list; let’s say that the key is "c" and the value is 3, so the list is ("c", 3). That list is assigned to the list ($key, $value), so $key becomes "c", and $value becomes 3.

But that list assignment is happening in the conditional expression of the while loop, which is a scalar context. (Specifically, it’s a Boolean context, looking for a true/false value, and a Boolean context is a particular kind of scalar context.) The value of a list assignment in a scalar context is the number of elements in the source list.

When Perl evaluates each %hash, and there are no more key-value pairs available, the each function will return an empty list.(It’s being used in list context, so it can’t return undef to signal failure; that would be the one-element list (undef) instead of the empty (zero-element) list ( ).) The number of elements of an empty list is 0, this is when the while condition returned false.

Typical Use of a Hash

The Bedrock library uses a Perl program in which a hash keeps track of how many books each person has checked out, among other information:

$books{"fred"} = 3; 
$books{"wilma"} = 1;

It’s easy to see whether an element of the hash is true or false; do this:

if ($books{$someone}) { 
    print "$someone has at least one book checked out.\n"; 
}

But there are some elements of the hash that aren’t true:

$books{"barney"} = 0; # no books currently checked out 
$books{"pebbles"} = undef; # no books EVER checked out - a new library card

Since Pebbles has never checked out any books, her entry has the value of undef, rather than 0.

The exists Function

To see whether a key exists in the hash, (that is, whether someone has a library card or not), use the exists function, which returns a true value if the given key exists in the hash, whether the corresponding value is true or not:

if (exists $books{"dino"}) { 
    print "Hey, there's a library card for dino!\n"; 
}
The delete Function

The delete function removes the given key (and its corresponding value) from the hash. (If there’s no such key, its work is done; there’s no warning or error in that case.)

my $person = "betty"; 
delete $books{$person}; # Revoke the library card for $person
Hash Element Interpolation

You can interpolate a single hash element into a double-quoted string just as you’d expect:

foreach $person (sort keys %books) { # each patron, in order
    if ($books{$person}) { 
        print "$person has $books{$person} items\n";  # fred has 3 items 
    } 
}

The %ENV hash

Your Perl program, like any other program, runs in a certain environment, and your program can look at the environment to get information about its surroundings. Perl stores this information in the %ENV hash. For instance, you’ll probably see a PATH key in %ENV:

print "PATH is $ENV{PATH}\n";

Most of these are set for you automatically, but you can add to the environment yourself. How you do this depends on your operating system and shell:

Bourne shell 
$ CHARACTER=Fred; export CHARACTER 
$ export CHARACTER=Fred 

csh 
% setenv CHARACTER=Fred 

DOS or Windows command 
C:> set CHARACTER=Fred

发表评论

电子邮件地址不会被公开。 必填项已用*标注