开发者

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; or local ($_) = @_; 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;
    $_;    
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜