ITworld.com
  Search  
ITworld Home Page ITworld Webcasts ITworld White Papers ITworld Newsletters ITworld News ITworld Topics Careers ITworld Voices ITwhirled Changing the way you view IT
Passing Subroutine References
PERL --- 08/23/2001

Andrew Johnson

Last week, we looked at using subroutine references for creating a dispatch table, this week we will consider passing subroutine references as arguments to other subroutines. 

On this topic

The ability to pass subroutine references is a powerful feature. Any subroutine you write that takes arguments can be thought of as a general routine and the arguments provide the specifics. For example, a subroutine that calculates the square root of a number is general, and the argument provides the specific number to calculate the square root from and return the result. Passing a subroutine reference is no different in that respect, but rather than passing something static like a number or a string, you can pass a definition of an action to take.

The File::Find module, which defines the find() routine (we saw this module back in February), is an excellent example. The find() routine is a general directory tree traversal subroutine whose first argument is a reference to a subroutine, followed by a list of directories to traverse. The find() routine traverses the listed directories and calls the subroutine reference for each directory entry found (it also provides a package global variable holding the current directory entry, for your subroutine to use). This gives the user of the module a very flexible means of accomplishing virtually any task involving directory traversal. Because the user supplies the intended action, the find() routine itself need only worry about the vagaries of traversing directories and none of the logic of detecting certain files, removing files, renaming files, or whatever else the user wishes to do.

You already know how to create a reference to a named function, and how to call a function through its reference -- there really isn't anything else you need to know to write a subroutine that takes a subroutine reference as an argument. Let's consider writing a reduce() routine in Perl (such a routine exists in the List::Util module on CPAN, which is implemented in C, but we will implement in Perl here).

A reduce() function takes a function reference as its first argument and then a list of values. It "reduces" the list by applying the passed function to the first two values, then to the result of that and the next value in turn. Thus, it could be used to easily produce sums of lists by supplying a function reference that simply adds two arguments together:

#!/usr/bin/perl -w
use strict; my @array = (1,2,3,4);

my $sum = reduce(\&add,@array);
print $sum;

sub add {
$_[0] + $_[1] }

sub reduce {
my $sref = shift; return @_ if @_ < 2; my $init = shift; for (@_) { $init = $sref->($init, $_); } return $init; }

We have taken the extra caution of simply returning the argument list without calling the sub reference if a list of only one element is passed to reduce().

The above produces a result of 10. Using the same reduce function we could produce the results of multiplying through the list, or dividing through the list, or any other action we created a function to do:

sub product { $_[0] * $_[1] }
sub divide { $_[0] / $_[1] }

my $product = reduce(\&product, @array);
my $division = reduce(\÷, @array);

We can avoid creating named subroutines by using anonymous subroutines (just like we can have anonymous arrays and hashes, we can have anonymous subroutines which are references to subroutines with no names). We create such a subroutine reference by using the 'sub' keyword followed immediately by the block of code (no name is specified):

my $sref = sub{print "Hello World\n"};
$sref->();

So now we can use reduce() as follows:

my $sum = reduce( sub{ $_[0] + $_[1] }, @array); my $product = reduce( sub{ $_[0] * $_[1] }, @array); my $division = reduce( sub{ $_[0] / $_[1] }, @array);

We could shorten that further using prototypes, but that is beyond the scope of the present article.

Next Week: Subroutine references as closures.

 

Andrew Johnson works as a programmer/consultant and is the author of Elements of Programming with Perl from Manning Publications.



Advertisements
Sponsored links
Bring harmony to your mix of UNIX-Linux-Windows computing environments
Top 5 Reasons to Combine App Performance and Security
KODAK i1400 Series Scanners stand up to the challenge
Locate Hidden Software on business PCs with this free tool
 Home   Newsletters  PERL
www.itworld.com    open.itworld.com     security.itworld.com     smallbusiness.itworld.com
storage.itworld.com     utilitycomputing.itworld.com     wireless.itworld.com

 
Contact Us   About Us   Privacy Policy    Terms of Service   Reprints  

CIO   Computerworld   CSO   GamePro   Games.net   IDG Connect   IDG World Expo   Industry Standard   Infoworld   ITworld   JavaWorld   LinuxWorld  MacUser   Macworld   Network World   PC World   Playlist  

Copyright © Computerworld, Inc. All rights reserved

Reproduction in whole or in part in any form or medium without express written permission of Computerworld Inc. is prohibited. Computerworld and Computerworld.com and the respective logos are trademarks of International Data Group Inc.