Perl logo


Perl Refreshments ⌘

References #1.2 - Subroutines

Hopefully you are getting comfortable with the concept of references. We know a couple of ways to create one, and how to use one. But one of the most powerful uses of a reference is when passing a largish array or hash to a subroutine.

Consider the situation where you have a large list of words, such as a dictionary. You want to do something with each word, for which you have written a subroutine. All you need to do is pass the dictionary to the subroutine. NOT!

One of the dictionaries that come with Unix / macOS systems has over 200,000 words. Imagine your poor subroutine trying to handle that.

Each time a subroutine is called, it has to copy any arguments being passed in. So the dictionary (each word being an argument) would have to make over 200,000 copies - one for each argument.

But passing a reference to the subroutine only needs 1 copy of the reference. This means your subroutine will run faster and is suitable for scaling up.

This may be hard to get your head around, but bear with me and see how it works.

For this adventure we're going to use the dictionary mentioned above (of course your list could be any list of words you have laying around).

All we're doing here is creating an array of words to work with ...

#! /usr/bin/perl
use strict;
use warnings;
$|=1; # disable buffering
BEGIN
{
	open (STDERR,">> $0.txt");
	print STDERR "\n", scalar localtime, "\n";
}

print "\n"x2;
print "References and subroutines\n\n";
my ($datafile,$line,$count,$countF,$match,$thisword,@words,$countAll);
($line=$count=$countAll=0);
#$datafile="/usr/share/dict/connectives";
$datafile="/usr/share/dict/words";
$match = "vad";
print "Looking for '$match' using '$datafile' ...\n";
# 3-argument file open
open (DF,"<",$datafile) or die "Can't open $datafile: $!\n";
while ($line = ()) {
	$countAll++;
	if ($line =~ m/$match/)	{
		push @words, $line;
		$countF++ ;
	}
}
close DF;
print qq{Now here's the array ...\n};
foreach $thisword (@words) {
	print qq{$thisword};
}
print qq{\nFound $countAll words\n};
print qq{$countF matching '$match' \t\n\n};

Copy and paste that into a new perl script and run it from your shell window.

My results may differ from yours, but you should see a list of words matching 'vad' along with the last 2 print statements. I have 53 words in my array.

Now that we have an array of words, we can return to our reference problem. We want to reverse the spelling of each of those words. Here is a subroutine to do that.

my $wordsRef = \@words; # initialize the reference variable & point it to the array
RevWords($wordsRef); # call the subroutine with the array reference

sub RevWords {
	my($inWord,$outWord);
	foreach $inWord (@$wordsRef) { # loop through the array reference
		chomp $inWord; # lose the newline
		$outWord = reverse($inWord); # DUH!
		print qq{$outWord } ; # add a space between words
	}
}

If you add the above code to the existing script, you should have a working script that calls the subroutine with a reference to an array.

My results:

refs5

This completes the exercise on how to pass a reference to a subroutine. Of course this is a very small example to use (a few dozen words) but the intention is just to show how it's done.

Note the special sigils used in the various situations. Don't be discouraged if it still seems a bit fuzzy.


-30-