Determining and Responding to the Calling Context - Page 21
July 27, 2001
Sometimes it is useful to know what the calling context is, so we
can return different values based on the caller's requirements.
The return statement already knows this implicitly,
and makes use of the context to save time, returning a count of a
returned list if the subroutine is called in a scalar context.
This is more efficient than returning all the values in the list
and then counting them passing back one scalar is simpler
when that is all the calling context actually requires.
Perl allows subroutines to directly access this information with
the wantarray function. Using wantarray
allows us to intelligently return different values based on what
the caller wants. For example, we can return a list either as a
reference or a list of values, depending on the way in which we
were called:
return wantarray? @files: \@files;
We can also use wantarray to return
undef or an empty list depending on context,
avoiding the problems of assigning undef to an array
variable as we discussed above:
return wantarray? (): undef;
Modifying our original subroutine to incorporate both these
changes gives us the following improved version of
list_files that handles both scalar and list
context:
sub list_files {
my $path = shift;
return wantarray? ():undef unless defined $path;
my @files = glob "$path/ *";
return wantarray? @files: \@files;
}
This is an example of Perl's reference counting mechanism in
action; @files may go out of scope, but the
reference returned in scalar context preserves the values it
holds.
We can now call list_files with two different
results. In list context we get either a list of files, or an
empty list if either no files were found, or the path is
undefined. This allows the return value to be used in a
foreach loop. If we want to distinguish between a
defined path with no files and an undefined path we call
list_files in scalar context. In return, we get a
reference to a list of files, a reference to an empty list if no
files were found, or the undefined value if the path was
undefined. By additionally testing for the undefined value with
defined, we can now distinguish all three cases:
# list context
@files = list_files ($ARGV[0]);
die "No path defined or no files found" unless @files;
print "Found: @files \n";
# scalar context
$files = list_files($ARGV[0]);
die "No path defined! \n" unless defined $files;
die "No files found! \n" unless $files;
print "Found: @{$files} \n";
One final note about wantarray: If we want to find
the number of files rather than retrieve a list, then we can no
longer call the subroutine in scalar context to achieve it.
Instead, we need to call the subroutine in list context and then
convert it into a scalar explicitly:
$count = $#{list_files $ARGV[0]}+1;
This is much clearer, because it states that we really do mean to
use the result in a scalar context. Otherwise, it could easily be
a bug that we have overlooked. However, be very careful not to
use scalar here. We often use scalar to
count arrays, but scalar forces its argument into a
scalar context. $# requires that its argument is a
list, and then counts it.
Handling Void Context
So far we have considered list and scalar contexts. If the
subroutine is called in a void context that is undefined. We can
use this fact to save time computing a return value, or even to
produce an error:
sub list_files {
die "Function called in void context" unless defined wantarray;
...
}
Returning the Undefined Value - Page 20
Professional Perl Programming
Handling Context: an Example - Page 22
|