Objective:
print "foobar"->gsub( qr/.(.)/, sub { shift() . $1 x 2 } );
__END__
foooobbbarrr
This is essentially what Ruby's gsub()
does. So the function reference should receive one parameter (namely the string matched by the pattern) and its return value will replace the matched string (for each occurance).
For obvious reasons, usage of $&
strictly forbidden.
Timelimit: 5 minutes.
Storage-complexity of resulting Perl code: O(1 line + $c) with 0 <= $c >= 4.
Would you guess that it took me about two hours and several miserable attempts (most of them involving lots of substr() and pos() operations) to do that? And even then, none of my solutions allowed to access $DIGIT
directly in the function (I had to pass them as parameters).
The correct (I hope) solution however looks marvelously simple and elegant. You certainly can't guess from it how long I fought with it:
sub gsub {
my ($string, $p, $s) = @_;
$string =~ s{$p}{ $sub->(substr $string, $-[0], $+[0] - $-[0]) }ge;
return $string;
}
Nonetheless, the idea of matching patterns iteratively and thus be able to hook functions into it is solver better in Ruby. Perl's s//CODE/ge
is not quite so nice. Things could improve once $&
becomes a variable without its current shortcomings.
COW
ethan on 2003-08-24T06:34:29
I heard contradictory things about COW so I am not sure. Isn't Nicholas the guy behind COW? He might know.
Meanwhile, I thought I could usetie()
to emulate$&
. It works with a tied hash actually (a scalar wont work), where the key is taken as the string against which the match was carried out:
#!/usr/bin/perl -w
package Tie::MATCH;
use Carp;
sub TIEHASH { bless \my $dummy => __PACKAGE__ }
sub STORE { croak "Variable is read-only" }
sub FETCH { return substr $_[1], $-[0], $+[0] - $-[0] }
package main;
tie my %MATCH, "Tie::MATCH";
my $var = "foobar";
$var =~ s/.*/$MATCH{$var} x 2/e;
print $var;
I kind of like this solution. With minimal modifications toTie::MATCH::STORE
it can be used to emulate$´
and$`
as well.