Re: multiple (different) substitutions on same $foo
by esh (Pilgrim) on Aug 22, 2003 at 05:07 UTC
|
I don't know if it is a good idea or not, but yes, it is possible.
for ( $foo ) {
s/frog/toad/g;
s/man/boy/;
s/woman/girl/;
}
Explanation: This is equivalent to the following where the default and magic $_ variable is implicit in several locations:
for $_ ( $foo ) {
$_ =~ s/frog/toad/g;
$_ =~ s/man/boy/;
$_ =~ s/woman/girl/;
}
Another magic thing going on here is that the $_ is aliased to the value(s) in the list so that the substitutions are actually modifying $foo.
--
Eric Hammond | [reply] [d/l] [select] |
|
Its picky, but I personally don't like the use of an iterative approach here (feels like "shoe-horning", kinda like the freaky NT/2000 command shell forces you to use "for" to get simple command substitution) with the "for" and might prefer a form that, nonetheless, still uses $_. I can easily see how folks might dislike that print stmt at the end, though, hmmmm....
$_ = "A man and a woman saw a frog by the pond\n";
s/frog/toad/g, s/man/boy/, s/woman/girl/, print $foo = $_;
| [reply] [d/l] |
|
Thanks for your reply, Eric. Why do you think it might not be a good idea?
| [reply] |
|
If your code is as simple as the sample you presented (three substitutions), then I believe your original style is probably faster and much clearer than the other alternatives proposed.
If you actually have dozens of substitutions to make, then I think it may be worth trying out one of the abstractions offered, though performance should be tested if it is a possible concern.
--
Eric Hammond
| [reply] |
|
|
Re: multiple (different) substitutions on same $foo
by dbwiz (Curate) on Aug 22, 2003 at 05:20 UTC
|
Yes. Not only that, you can also print the result in one go.
my $foo = "A man and a woman saw a frog by the pond\n";
s/frog/toad/g, s/man/boy/, s/woman/girl/, print for ($foo);
__END__
A boy and a girl saw a toad by the pond
update This is beyond the point of the original request, but following halley's lead, a stronger implementation would be
s/\bfrog\b/toad/g, s/\bman\b/boy/, s/\bwoman\b/girl/, print for $foo;
| [reply] [d/l] [select] |
|
Order is also important in our (possibly quite contrived) example. Change it around just a bit and you see the problem:
my $foo = "A woman and a man saw a frog by the pond\n";
s/frog/toad/g, s/man/boy/, s/woman/girl/, print for ($foo);
__END__
A woboy and a man saw a toad by the pond
-- [ e d @ h a l l e y . c c ] | [reply] [d/l] |
Re: multiple (different) substitutions on same $foo
by Daruma (Curate) on Aug 22, 2003 at 05:26 UTC
|
Greetings!
You could put your pairs into a hash for easy reference...
use strict;
use warnings;
my $foo = "The frog kissed the woman and became a man.";
print "$foo\n";
my %replace = (
"frog" => "toad",
" man" => " boy",
"woman" => "girl"
);
foreach my $find (keys %replace) {
$foo =~ s/$find/$replace{$find}/;
}
print "$foo\n";
-Daruma
Update changed variable names for easier reading... | [reply] [d/l] |
|
my %repl= ( frog=>"toad", man=>"boy", woman=>"girl" );
$foo =~ s/(frog|man|woman)/$repl($1)/g;
And you can even build the regex from the keys, if you like:
my $re= join "|", map quotemeta($_), keys %repl;
But the original code is probably both faster and simpler. (:
- tye | [reply] [d/l] [select] |
|
However, the original poster specified one /g and two non-/g replacements. If that is the real intent, then a data-table solution would need to flag the repeatable replacements versus the singular replacements. If they all should be repeatable, then the data solutions offered are very useful.
Also, a note to the original poster: s/man/boy/ would create a lot of strings like "read the fine boyual" in a general text. You may want to learn what the zero-width assertion \b does inside a regex. s/\bman\b/boy/g; s/\bmen\b/boys/g may help you out.
-- [ e d @ h a l l e y . c c ]
| [reply] [d/l] [select] |
Re: multiple (different) substitutions on same $foo
by cleverett (Friar) on Aug 22, 2003 at 05:32 UTC
|
Do it something like this way:
%sub = ( frog => 'toad', man => 'boy', woman => girl );
$exp = join('|', keys %sub);
$foo =~ s/($exp)/$sub{$1}/g;
and you would be able to do the substitution at multiple points in your code, but only define what gets substituted once. | [reply] [d/l] |
Re: multiple (different) substitutions on same $foo
by davido (Cardinal) on Aug 22, 2003 at 05:17 UTC
|
Here is one way to do it. In the case of only three $foo =~ matches this method seems foolish, but if your list of matches and substitutions is long enough, it's worthwhile. It is also possible to put the pattern as a hash key and the substitution as a hash value, or vice versa. Then iterate through "each %hash".
foreach (qq/frog toad/, qq/man boy/, qq/woman girl/) {
my ( $pattern, $substitution) = split /\s/, $_;
$foo =~ s/\Q$pattern\E/$substitution/g;
}
Another way would be like this:
my %testhash = ( qw/frog toad man boy woman girl/ );
foreach (keys %testhash) {
$foo =~ /\Q$_\E/$testhash{$_}/;
}
Note that I added "\Q" and "\E" to both regexps. This is to ensure that the variable being interpolated into the regexp doesn't have any possible metacharacters interpreted in the regexp. So if you happen to be testing 'C:\data' (a lousy example, but stay with me), the regexp engine won't see it as 'C:[0-9]ata'.
In your example, the \Q and \E quotemeta function isn't needed. And there are plenty of cases where its function is undesirable (for example, what if you WANT 'C:\data' to be interpreted as 'C:[0-9]ata'), but I included them in my example to ensure that you are matching in the most simple way.
I don't see any way of just having one iteration of $foo =~ s/// do it all though.
Dave
"If I had my life to do over again, I'd be a plumber." -- Albert Einstein | [reply] [d/l] [select] |
Re: multiple (different) substitutions on same $foo
by chunlou (Curate) on Aug 22, 2003 at 05:40 UTC
|
| [reply] |
Re: multiple (different) substitutions on same $foo
by matsmats (Monk) on Aug 22, 2003 at 14:48 UTC
|
Here's an alternative way to do it, doing the whole search and replace as a oneliner:
my $foo = "The frog kissed the woman and became a man.";
$foo =~ s/\b(frog|woman|man)\b/{frog=>toad,woman=>girl,man=>boy}->{$1}
+/eg;
I've added a \b for you, see comment further up
This assumes you meant to have a /g-switch after every substitution. I couldn't find a way to match only the first "man" and "woman", but I'm sure it will keep me awake tonight.
| [reply] [d/l] |
or just wait for perl 6
by jkahn (Friar) on Aug 24, 2003 at 05:13 UTC
|
According to Apocalypse 4, which reads:
Basically, the only difference between a given and a for is that a given takes a scalar expression, while a for takes a pre-flattened list and iterates over it.
given $foo {
s/frog/toad/g;
s/man/boy/;
s/woman/girl;
}
Hurry up, Larry! | [reply] [d/l] [select] |
Re: multiple (different) substitutions on same $foo
by traveler (Parson) on Aug 22, 2003 at 22:54 UTC
|
I think the OP wanted simplification, not just removal of $foo. I offer:
$_ = $foo;
s/frog/toad/g;
s/man/boy/;
s/woman/girl/;
One could then assign $_ back to $foo or use $_ directly as in print;
--traveler | [reply] [d/l] [select] |