Reputation: 38714
I have this trim
routine:
sub trim {
for (@_) {
s|^\s+||;
s|\s+$||;
}
}
It trims whitespace "in-place", i.e.:
trim $x, $y;
will trim the whitespace of $x and $y (modifying them).
How can I improve trim
so that it lets me call it like:
trim;
and that will be the same as:
trim $_;
?
Upvotes: 8
Views: 857
Reputation: 386461
If your trim
just took one arg, you could use the _
prototype.
sub trim(_) {
for ($_[0]) {
s|^\s+||;
s|\s+$||;
}
}
Then all of these would work:
{ local $_ = " abc "; trim; say; }
{ my $_ = " abc "; trim; say; }
{ my $x = " abc "; trim $x; say $x; }
But since your trim
takes a list of args, you can't use the _
prototype. You have to use @_ ? @_ : $_
.
sub trim {
for (@_ ? @_ : $_) {
s|^\s+||;
s|\s+$||;
}
}
But that means it won't work with lexical $_
.
{ local $_ = " abc "; trim; say; }
{ my $_ = " abc "; trim; say; } # XXX $::_ changed instead of $_
{ my $x = " abc "; trim $x; say $x; }
Upvotes: 4
Reputation: 40152
All you need to do is replace @_
with @_ ? @_ : $_
in the argument to the for
loop.
sub trim {
for (@_ ? @_ : $_) {
s|^\s+||;
s|\s+$||;
}
}
Per ikegami's answer, there is the nagging issue of what to do about the blight that is lexical $_
(declared with my $_;
) since that version of $_
is not global, but bound to a lexical pad.
The (_)
prototype is one way to solve this problem, but it also means that the subroutine only takes one argument, and that the argument has scalar context, which makes it just as bad as the ($)
prototype (due to unintended consequences of scalar context).
Whenever trying to do magic on lexicals, the module PadWalker comes in handy. So here is a version that works correctly on lists, works on both lexical and global $_
, and it does not impose scalar context on the call site:
use PadWalker 'peek_my';
sub trim {
my $it;
unless (@_) {
my $pad = peek_my 1;
$it = $$pad{'$_'} || \$::_
}
for (@_ ? @_ : $$it) {
s/^\s+//;
s/\s+$//;
}
}
{local $_ = " a "; trim; say "[$_]"} # prints "[a]"
{my $_ = " a "; trim; say "[$_]"} # prints "[a]"
{my $x = " a "; trim $x; say "[$x]"} # prints "[a]"
Personally, I just avoid lexical $_
and ugly problems like this just disappear. But if you need to support it, this is a way.
Upvotes: 11
Reputation:
Just check to see if there are no arguments provided:
use strict;
use warnings;
$_=" abc ";
sub trim
{
if(@_)
{
foreach my $i(@_)
{
$i=~s/^\s+//;
$i=~s/\s+$//;
}
}
else
{
s/^\s+//;
s/\s+$//;
}
}
trim;
print "!$_!\n"; #Throwing in exclamation points just to make sure white space is gone.
This produces the output
!abc!
Upvotes: 11