How can I golf this Perl subroutine that does a substitution?
I have the following subroutine in Perl to substitute "abc" for "xyz" in a string:
sub mySubst {
my ($str) = @_;
$str =~ s|abc|xyz|ig;
return $str;
}
It works, but see开发者_JS百科ms way too verbose for Perl. How can I tighten it up?
What you have is fine.
- You pull the arguments off the @_ variable, making a copy, using a list assignment. List assignment is an excellent way to do it (and basically the standard way.) Using
shift
would also work, but changes @_ (may or may not be what you want.) There's a discussion on PerlMonks about shift vs @_ that you might be interested in. - You use a named variable with your search and replace. I prefer named variables in this case, since dealing with Perl's magic variables takes care.
- Automatically working on $_ would be possible, but not having $_ auto populated makes it trickier to get right. You'd need to do
local $_ = shift;
orlocal ($_) = @_;
which doesn't add much.) - I like it when people use an explicit
return
. It's a warm fuzzy. - K&R Brackets. Good. :)
- No prototype. Very good. :)
Go with it. I think you're on the right track.
If you're looking for golf, and not production code.
use strict; ## Not really required ;)
use warnings; ## Not really required ;)
sub f{local$_=pop,s/foo/bar/ig;$_}
print f 'foobarbaz';
This is how I would render that subroutine more idiomatically:
sub mySubst {
(my $str = shift) =~ s|abc|xyz|ig;
return $str;
}
You could write:
sub mySubst { (map { s|abc|xyz|ig; $_ } "$_[0]" )[0] }
but unless this is an exercise in obfuscation, I would say go with what you have. Remember, you are not writing the program for the computer.
I think too verbose sounds like not enough obfuscation which I disagree. As for tightening it up I'd suggest something along the lines of:
sub replaceBeginningWithEnd {
my $text = shift;
return if not defined($text);
$text =~ s/abc/xyz/ig;
return $text;
}
- Make names more readable
- Use conventions (ie. / as oppose to |)
- Argument checking
No one gave the Perl idiom for this yet. This replicates the behavior of the original code by modifiying a copy:
(my $new_str = $old_str) =~ s/abc/xyz/ig;
If I had to put that into a subroutine, which I think is kinda silly for such a simple operation, I guess that would be:
sub foo { (my $s = $_[0]) =~ s/abc/xyz/ig; $s }
However, if I was doing something silly like this, I wouldn't make a named subroutine for it. I'd make a subroutine to make the subroutine for me so I don't need a new named sub for every possible sort of replacement:
sub make_sub {
my( $regex, $replacement ) = @_;
# season to taste with error checking
sub {
(my $s = $_[0]) =~ s/$regex/$replacement/ig; $s
};
}
my $replacer = make_sub( qr/abc/, 'xyz' );
my $new = $replacer->( $string );
what is the sub for? just do it like this
$str =~ s|abc|xyz|ig;
To avoid changing the originals, you could write
sub mySubst { map { (my $s=$_) =~ s|abc|xyz|ig; $s } @_ }
or
sub mySubst { my @a = @_; map { s|abc|xyz|ig; $_ } @a }
or to borrow from Sinan's answer, except discarding the temporary array explicitly:
sub mySubst { map { s|abc|xyz|ig; $_ } my(undef) = @_ }
but the syntax is still a little noisy. Because it uses map
, be sure to call it in list context:
my($result) = mySubst $str; # NOT my $one = mySubst $str;
If you expect to mostly call mySubst
with a single argument but want to handle cases of one or more arguments, then you could write
sub mySubst {
s|abc|xyz|ig for my @a = @_;
wantarray ? @a : $a[0];
}
but that starts up the stuttering again.
If you want to update the parameter itself, use the alias semantics of Perl's subs as documented in perlsub:
The array
@_
is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element$_[0]
is updated, the corresponding argument is updated (or an error occurs if it is not updatable).
So you could write
sub mySubst { $_[0] =~ s|abc|xyz|ig }
or even
sub mySubst { map { s|abc|xyz|ig; $_ } @_ }
Example usage:
$str = "fooabcbar";
mySubst $str;
print $str, "\n";
Output:
fooxyzbar
You can omit the return keyword:
sub mySubst {
my ($str) = @_;
$str =~ s|abc|xyz|ig;
$str;
}
It is also possible to use the default variable ($_
) :
sub mySubst {
local $_ = shift; # or my starting from Perl 5.10
s|abc|xyz|ig;
$_;
}
精彩评论