Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

multiple (different) substitutions on same $foo

by jonnyfolk (Vicar)
on Aug 22, 2003 at 04:50 UTC ( [id://285668]=perlquestion: print w/replies, xml ) Need Help??

jonnyfolk has asked for the wisdom of the Perl Monks concerning the following question:

I'd like to make the following substitutions:
$foo =~ s/frog/toad/g; $foo =~ s/man/boy/; $foo =~ s/woman/girl/;
Is it possible to do it without the repitition of $foo =~ ?

Replies are listed 'Best First'.
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

      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 = $_;
      Thanks for your reply, Eric. Why do you think it might not be a good idea?

        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

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;
      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 ]

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...

      For a second, I thought you were suggesting what I was going to suggest... Close:

      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

        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 ]

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.
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

Re: multiple (different) substitutions on same $foo
by chunlou (Curate) on Aug 22, 2003 at 05:40 UTC
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.

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!

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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://285668]
Approved by Paladin
Front-paged by graff
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (2)
As of 2024-04-20 03:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found