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 } }

In reply to Intelligently shortcutting $_-or-params, non-destructive-or-in-place function maker by Aristotle

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.