I often want to write short utility functions that do things like running a simple but frequently needed substitution on strings or the like. For these functions, I often want shortcutting behaviour: operate on $_ if not given a parameter, and operate destructively in-place if not asked to return values.
Writing functions that work like this is a real pain in the neck: there are multiple cases to be considered, you have to be careful to preserve the aliasing nature of @_ and $_, etc. It’s way too much work to keep doing it over and over.
So here’s a function maker that lets you build such functions, carefully written to rely entirely on aliasing, rather than jumping through any reference-taking hoops. It expects to be given a reference to a function which always operates destructively on its parameters (ie modifies the elements of @_), and from this builds a function that will default to $_ as its input and will either return modified copies or modify its operands in-place.
Eg.:
BEGIN { *basename = shortcutted { s!.*/!! for @_ }; }
for( '/path/to/foo', '/some/path/to/bar' ) {
print "Munging " . basename . "\n";
open my $fh, '<', $_ or die $!;
# note how $_ still contains the full pathname
# ...
}
sub shortcutted(&) {
my $sub = shift;
sub {
my @byval;
my $nondestructive = defined wantarray;
$sub->( $nondestructive
? ( @byval = @_ ? @_ : $_ )
: ( @_ ? @_ : $_ )
);
return $nondestructive ? @byval[ 0 .. $#byval ] : ();
};
}
use Test::More;
sub original() { 'original' }
sub modified() { 'modified' }
my $test = shortcutted { $_ = modified for @_ };
plan tests => my $num_tests;
{
local $_ = original;
$test->();
is( $_, modified, 'in-place on $_' );
BEGIN { $num_tests += 1 }
}
{
local $_ = original;
my $res = $test->();
is( $_, original, 'nondestructive from $_' );
is( $res, modified, '...returned correctly' );
BEGIN { $num_tests += 2 }
}
{
my $num = 10;
my @original = ( original ) x $num;
my @modified = ( modified ) x $num;
$test->( my @data = @original );
is_deeply( \@data, \@modified, 'in-place on params' );
BEGIN { $num_tests += 1 }
}
{
my $num = 10;
my @original = ( original ) x $num;
my @modified = ( modified ) x $num;
my @res = $test->( my @data = @original );
is_deeply( \@data, \@original, 'non-destructive from params' );
is_deeply( \@res, \@modified, '...returned correctly' );
BEGIN { $num_tests += 2 }
}
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.