March 22, 2004 (Monday)

Subroutines (S&P — Chapter 4, cont'd)

Calling Functions using Parentheses

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 (S&P — Chapter 5)

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 = ("", "garfield",
		  "", "mirror",
		  "", "phobos", );

$ip_to_host{""} = "deimos";
$ip_to_host{""} = "irma";

while (my ($ip, $host) = each %ip_to_host) {
	print "Hostname $host has IP address $ip\n";
print "\n";
delete $ip_to_host{""}; # 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";

The above script demonstrates many aspects of hash types in Perl.

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.

Input/Output (S&P — Chapter 6)

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>)) {
	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>)) {
	print "$_\n";

Perl allows you to expresses this more succinctly as:

while (<STDIN>) {
	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)