March 19 (Friday) March 24 (Wednesday)
There is a 'gotcha' in Perl related to functions that take lists
as parameters (especially the print
function). If
you use parenthesis to enclose the arguments of a function that
takes a list, then make sure that the parenthesis enclose all
the arguments, otherwise things may not behave as you expect.
For example, let's say we wanted to add two numbers and multiply
the result by a third number and display the result:
print (2+3)*4;
This code does not quite do what one would expect. Because parentheses
are used, the print
function assumes that all of its arguments are
inside the parentheses. The 2+3
are added together, the
result, 5
is passed to print
which causes
the value 5
to be displayed. Because print
is a function, it returns a value (normally 1, if everything was displayed
okay). This return value is then multiplied by 4
and the
result of the multiplication is not used anywhere (i.e. a void
context). To correctly print out the result of the arithmetic expression,
put parentheses around everything that print
is to display:
print ((2+3)*4);
Again, the -w
option to perl
is very helpful
in finding these sorts of errors.
Hashes in Perl are roughly equivalent to the map
class
of the C++ standard library. They associate scalar strings (called
a key) to their respective values (typically,
another scalar). Unlike the map
class, Perl's hashes
are not self-ordering. By default, the order in which you place the
elements into the hash may be different than the order in which you can
retrieve them. Perl does provide a way to retrieve the keys in order,
but you have to do so manually, as seen below:
#!/usr/bin/perl -w
use strict;
my %ip_to_host = ("134.153.48.1", "garfield",
"134.153.48.2", "mirror",
"134.153.48.3", "phobos", );
$ip_to_host{"134.153.48.4"} = "deimos";
$ip_to_host{"134.153.48.10"} = "irma";
while (my ($ip, $host) = each %ip_to_host) {
print "Hostname $host has IP address $ip\n";
}
print "\n";
delete $ip_to_host{"134.153.48.2"}; # Get rid of 'mirror' host.
# Display the hosts again, this time in sorted order. Note
# that the sorting is lexicographic. So host irma's IP
# address appears after garfield but before phobos
#
for (sort keys %ip_to_host) {
print "Hostname $ip_to_host{$_} has IP address $_\n";
}
my %host_to_ip = reverse %ip_to_host;
printf "\nGarfield has ip address $host_to_ip{'garfield'}\n";
hash1.pl
The above script demonstrates many aspects of hash types in Perl.
my %ip_to_host = ("134.153.48.1" => "garfield", "134.153.48.2" => "mirror", "134.153.48.3" => "phobos", );
This way, it is much easier to see which element in the list is the key and which is the value. Also, specifying one key/value pair per line makes it clear which entities are keys and which are values.
$ip_to_host{"134.153.48.4"} = "deimos"; $ip_to_host{"134.153.48.10"} = "irma";
Note that if we had used square brackets, then this would be treating the
ip_to_host
as a (different) array variable and not a hash.
A warning would then be generated since the index is not entirely numeric
and because the array @ip_to_host
was not declared with
my
.
while
loop demonstrates one way to iterate over
the key/value pairs in a hash using the each
function.
The each
function will return a two-element list consisting
of successive key/value pairs stored in the hash. When it comes to the
end of the hash, an empty list is returned and the while
loop terminates. We assign the result of each list returned by
each
to the $ip
and $host
variables
(in list context).
delete
function as demonstrated by the above code. We can
also check for whether a particular key exists in a hash by using the
exists
function in a similar way. The exists
function will return true if the specified key exists. Note that
this is different from the defined
function. Consider the
following Perl code:
my %h = ('hello' => undef); print defined $h{'hello'}, "\n"; # false print exists $h{'hello'}, "\n"; # true
defined
used in the above context determines if the value
associated with the key 'hello'
is defined. Because the
value is undef
, this returns false since undef
is not defined (by definition). exists
merely tests to
see if the key exists in the hash (it doesn't consider the value).
If the key exists, then it returns true.
for
loop demonstrates a way that we can iterate
over the hash accessing the elements sorted by key. To do so we use
the keys
function on the hash. This returns a list containing
all the keys stored in the hash table (a similar function called
values
returns a list containing all the values in the hash).
We then sort this list using the sort
function and iterate
over this sorted list. Note that we are using the default variable
$_
to store each key on each iteration through the loop.
sort
does a lexicographical ordering on the
list, so keys that contain numeric-like values (as above) will not be
sorted numerically.
reverse
function on the hash variable. Note that if all the
values in the original hash table are not unique, then the resulting
hash table will have fewer elements that the original.
One final note about hashes is that hashes are not interpolated inside
double quotes, like scalars and arrays. Therefore, you cannot, for
example, display the keys/values of a hash variable by saying print
"The hash table is %h\n"
. The %h
will be interpreted
literally.
There are several ways to read input from a file. One way is is to use
the <STDIN>
operator in list context:
chomp (my @list = <STDIN>);
This will read all the lines from standard input and store each one in
the @list
array. All the newlines will then be removed
from each line by the chomp
function. Unfortunately,
if the file is large then the list array could consume quite a bit
of memory. In Perl, it is common to process files one line at a time.
To do this, we can rely on the fact that the <STDIN>
operator returns undef
when all the input has been consumed:
while (defined (my $line = <STDIN>)) { chomp($line); print $line, "\n"; }
If we want, we can use the special default variable $_
to store each line of the input as we read it:
while (defined ($_ = <STDIN>)) { chomp($_); print "$_\n"; }
Perl allows you to expresses this more succinctly as:
while (<STDIN>) { chomp; print; print "\n"; }
When the line input operator, <STDIN>
, is used in the context
of a while
condition, Perl will assign the result of the
line to the default variable $_
and that can be used
inside the body of the loop. In addition, for many Perl functions,
(e.g. chomp
, length
and print
),
if you do not specify an argument, then they will work on $_
,
by default. Therefore, the call to chomp
above is acting
on $_
. Similarly, the call to print
will display
the contents of the $_
variable.
Note that just writing <STDIN>
alone does not cause assignment to the
default $_
variable — the assignment to $_
only happens when <STDIN>
is in the context of the while
conditional.
Last modified: March 23, 2004 00:44:52 NST (Tuesday)