Previous Page
Next Page

3.10. Utility Subroutines

Prefix "for internal use only" subroutines with an underscore.

A utility subroutine exists only to simplify the implementation of a module or class. It is never supposed to be exported from its module, nor ever to be used in client code.

Always use an underscore as the first "letter" of any utility subroutine's name. A leading underscore is ugly and unusual and reserved (by ancient C/Unix convention) for non-public components of a system. The presence of a leading underscore in a subroutine call makes it immediately obvious when part of the implementation has been mistaken for part of the interface.

For example, if you had a function fib( ) for computing Fibonacci numbers[*] (like the one shown in Example 3-1), then it would be an error to call:

[*] Much like the weather, everybody talks about Fibonacci numbers, but nobody ever seems to do anything with them. That's a pity, because the Fibonacci sequence does have important real-world uses: reallocating increasing amounts of memory for data structures whose ultimate size is unknown; estimating reasonable timeouts under uncertain connectivity; scaling retry intervals to resolve lock contentions; or anywhere else that you need to keep retrying some operation, but want it to be bigger, longer, or slower each time.

    print "Fibonacci($n) = ", _find_fib($n), "\n";

because _find_fib( ) doesn't return a useful value. You almost certainly wanted:

    print "Fibonacci($n) = ", fib($n), "\n";

By naming _find_fib( )with an initial underscore, the call to it stands out far more clearly, and the misuse is brought immediately to the attention of anyone familiar with the convention.

Example 3-1. Iterative on-demand Fibonacci computations

# Cache of previous results, minimally initialized...
my @fib_for = (1,1);
# Extend cache when needed...
sub _find_fib { my ($n) = @_;
# Walk up cache from last known value, applying Fn = Fn-1 + Fn-2...
for my $i (@fib_for..$n) { $fib_for[$i] = $fib_for[$i-1] + $fib_for[$i-2]; } return; }
# Return Fibonacci number N
sub fib { my ($n) = @_;
# Verify argument in computable range...
croak "Can't compute fib($n)" if $n < 0;
# Extend cache if necessary...
if ( !defined $fib_for[$n] ) { _find_fib($n); }
# Look up value in cache...
return $fib_for[$n]; }

    Previous Page
    Next Page