15 Part 14: References

LinuxChix Perl Course Part 14: References

1) Introduction
2) Learning About Scalars - Again!
3) A List of Lists
4) An Array of Hashes
5) Where to Find Out More
6) Exercises
7) Answers to Previous Exercises
8) Acknowledgements
9) Licensing


----------------------------------------

1) Introduction

The programs that we've been writing are fairly simple, but you may
have already run up against the limitations imposed by flat lists.
For example, we might want to read the password file and convert each
line into a list of fields, but we have no good way to join the lists
into a list of lists representing the whole file.

With references we're going to solve that problem. There are lots of
uses for references, including:
a) creating an array of arrays, or a hashtable of arrays, or a
hashtable of hashtables.
b) passing several distinct arrays to a function, or returning
several distinct arrays.
c) creating a "function pointer", allowing us to chain
tranformations.
d) writing our own function that can take code blocks, allowing us to
extend the language (sort of).

This lesson is rather long and counterintuitive, but try to stick
with it to the end. References aren't as much fun as some other
concepts (like s///) but they're essential to writing non-trivial
programs, and in particular for object-oriented programming in Perl.

----------------------------------------

2) Learning About Scalars - Again!

Recall that there are four basic data types in Perl. They are:
a) scalar - A single chunk of data
b) array - A bunch of scalars, indexed by a number
c) hash (hashtable) - A bunch of scalars, indexed by another scalar
d) handle - A pointer enabling the opening of resources from the
operating system (files, directories, etc.)

We have seen all four types. One would think that the simplest type,
scalars, would hold no more secrets from us. But there is one type of
scalar that we haven't seen yet: a reference.

Try running this program:

my $x = 'foo';
my $r = \$x;
${$r} = 'bar';
print '$x = ' . "$x\n"; # Output is: bar
print '${$r} = ' . "${$r}\n"; # Output is: bar

As you can see, "$r" is a pointer to "$x". But in Perl it's called a
reference, and it doesn't have any of the painful properties
associated with pointers in C++. You can't generate a memory leak in
Perl even if you try. In that sense, Perl's references are much more
like Java's pointers than C++'s pointers.

Because a reference is a scalar, it can be stored in an array. Which
is where most of its utility comes from, as we'll see next.

----------------------------------------

3) A List of Lists

The following code creates a reference to an array:

my @a = ('foo', 'bar', 'baz');
my $r = \@a;
${$r}[1] = 'x'; # Replace "bar" with "x".
$r->[2] = 'y'; # Replace "baz" with "y" (nicer syntax).
print "@a\n";

Knowing this, we can create an array of arrays.

my @main_list;
for (my $i=0; $i<5; $i++) {
my @sub_list = ('foo', 'bar');
$main_list[$i] = \@sub_list;
}
print "${$main_list[1]}[0]\n"; # Output is "foo"
print "$main_list[1]->[0]\n"; # Shorter form of above.
print "$main_list[1][0]\n"; # Shortest form of above.

NOTE: "@sub_list" is declared inside the loop, so each iteration
creates a new variable. If it was declared outside the loop, every
array index would point to the same variable, and that probably isn't
what you want.

The final optimisation we can perform on the syntax is replacing the
"\@sub_list" with a list in square brakets:

for (my $i=0; $i<5; $i++) {
$main_list[$i] = ['foo', 'bar'];
}

The square brackets generate a reference to a list without using an
intermediate temporary variable.

----------------------------------------

4) An Array of Hashes

As you might have guessed, we can create a reference to a hash as
follows:

$r = \%h;

Just as square brackets create a reference to a list, braces create a
reference to a hash. So we can create a list of hashes like this:

my @list_of_hashes;
for (my $i=0; $i<5; $i++) {
$list_of_hashes[$i] = { 'tea'=>'coffee', 'pear'=>'lemon' };
}
print "$list_of_hashes[1]{pear}\n"; # Output is "lemon"

----------------------------------------

5) Where to Find Out More

Hashes aren't as intuitive as some other aspects of Perl, but we've
gotten through the basics. You'll find more uses for references as
you read other people's code and documentation.

If you want to dig in further, here are some good places:

perldoc perlreftut
perldoc perlref
perldoc perldsc
perldoc perllol
perldoc -f ref

----------------------------------------

6) Exercises

a) Write a Perl program that reads "/etc/passwd" and creates an array
of arrays of fields such that "$passwd[$i][$j]" represents line "$i",
field "$j". (The first field is the username, the second field is the
password, etc.) How would you search this array for all the users who
use a given shell?

b) Change the previous program so that it uses an array of hashes
instead, with each user account a hash mapping "username" to
username, "uid" to UID, etc. Now how will you implement a search?

c) Consider the following program:

#!/usr/bin/perl -w
use strict;

my $s = 'a scalar';
my $r1 = \$s;
my $r2 = ['a', 'reference', 'to', 'an', 'array'];
print ref($r1);
print ref($r2);
print ref($s);

what does the "ref" function do? Where do you think it's usually
used?

d) Perl has another mechanism for references, illustrated as follows:

$foo = 'xyz';
$bar = 'foo';
print $$bar; # output is: xyz

(By the way, Perl won't let you do this if you "use strict".)

This is the only reference mechanism that existed before Perl 5. Why
is Perl 5's mechanism such an improvement?

e) Consider the following code:

#!/usr/bin/perl -w
use strict;

sub make_uppercase {
my @result = @_;
foreach my $item ( @result ) {
$item = uc($item); # uc = upper-case
}
return @result;
}

my $transformation = \&make_uppercase;
my @transformed = &$transformation('foo', 'bar');
print "@transformed\n";

What does the "\&" operator do?

f) Consider this code:

#!/usr/bin/perl -w
use strict;

sub DEBUG() {
return 1;
}

sub do_if_in_debug_mode (&) {
my $func = shift;
&$func if DEBUG;
}

do_if_in_debug_mode {
print "Just testing.\n";
};

Now write your own "extention" to the Perl language.

----------------------------------------

7) Answers to Previous Exercises

a) The first exercise was to write a function to do what one might
expect this to do:

@arr = map(reverse, @arr); # Doesn't work.

and then maybe to explain why the above code doesn't work as
expected.

Here's a function that will do what we want:

sub scalar_reverse {
my @result = @_;
foreach my $element (@result) {
$element = reverse($element);
}
return @result;
}

As for why the code with "map" doesn't work, the reason is the
difference between scalar context and list context. "reverse" in
scalar context reverses the order of the characters in its input, but
in list context it does something different. "map" always calls a
function in list context. To override this, use the "scalar" keyword:

my @arr = ('foo', 'bar');
@arr = map {scalar reverse} @arr;
print "@arr\n"; # Output is: oof rab

b) In this declaration:

sub foo($$$)

The part in parentheses is called the prototype. In this case, it
specifies that it is expecting three scalar parameters. Perl will
complain if you provide more or fewer, or even if you provide an
array with exactly three elements.

Incidentally, some people prefer to write a "normal" (unlimited
arguments) Perl function like this:

sub foo(@)

c) The "wantarray" function returns true if its containing function
was called in list context. It returns false if the function was
called in scalar context, and it returns the undefined value if the
function was called in void context (i.e., the return value is not
used).

----------------------------------------

8) Acknowledgements

A big thank you to Jacinta Richardson for suggestions and
proofreading. More advanced Perl users might want to check out the
free material from Perl Training Australia
<http://www.perltraining.com.au/>, which she is a part of.

Other contributors include Meryll Larkin.

----------------------------------------

9) Licensing

This course (i.e., all parts of it) is copyright 2003-2005 by Dan
Richter and Alice Wood, and is released under the same license as
Perl itself (Artistic License or GPL, your choice). This is the
license of choice to make it easy for other people to integrate your
Perl code/documentation into their own projects. It is not generally
used in projects unrelated to Perl.