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

Is there an idiom (besides using temp variables) for doing substitution without modifying the original string's value?

print s/foo/bar/g; will print the number of changes made to $_, and $_ will be permanently modified. I'd prefer a construct that returned the modified string, which you could optionally feed back into the original variable.

Does this already exist and I just don't know about it? Or am I doomed to use temp variables for all eternity?

---
A fair fight is a sign of poor planning.

Replies are listed 'Best First'.
Re: Non-destructive string substitution
by fruiture (Curate) on Apr 20, 2004 at 18:29 UTC

    The key is to copy the data you want to preserve. If you need this kind of behaviour very often, write a simple subroutine that does what you want.

    sub cdo(&$){ local $_ = $_[1]; $_[0]->(); $_ } my $foo = 'abc'; print cdo { s/abc/def/ } $foo; print "\n$foo\n";

    TI, of course, MTOWTDI.

    --
    http://fruiture.de
Re: Non-destructive string substitution
by japhy (Canon) on Apr 20, 2004 at 17:59 UTC
    This is one way: map { s/this/that/; $_ } do { $str }; You can substitute [$str]->[0] for do { $str } if you'd like.
    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;
      This doesn't work either:
      my $foo = "foo"; print map {s/foo/bar/g; $_} do{$foo}; print "\n$foo\n";
      prints
      bar bar
      This, however, does:
      my $foo = "foo"; print map {s/foo/bar/g; $_} @{[$foo]}; print "\n$foo\n";

      -Mark

        Hmm. The debugger runs them differently. Oh well. Regardless, if you don't mind stringification, you can use "$foo" as the argument to map().
        _____________________________________________________
        Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
        s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;
Re: Non-destructive string substitution
by Fletch (Bishop) on Apr 20, 2004 at 17:44 UTC
    print do { (my $t = $_ ) =~ s/foo/bar/g };

    Update: Ah, missed the without temp variables qualifier. You could use the similar do { local($_)=$_; s/foo/bar/g }, which uses the block's stack rather than an explicit temporary.

Re: Non-destructive string substitution
by QM (Parson) on Apr 20, 2004 at 19:16 UTC
    Does this generalize into a quest for an anonymous scalar? [Note: the OP is asking for new scalar either way -- only the name is eschewed.]

    Is there any benefit to avoiding the named temp variable? Speed? Obfu?

    I would prefer to see the autodocumenting nature of

    ($bar = $foo) =~ s/foo/bar/g;
    In many cases the new value will be caught in another variable anyway. I'm guessing 'no temp' is only interesting if the value is passed to another operator/function immediately, like print or perldoc:length, for instance.

    It's also not a trivial exercise to devise a correctly formed Benchmark that demonstrates the difference.

    -QM
    --
    Quantum Mechanics: The dreams stuff is made of

Re: Non-destructive string substitution
by kvale (Monsignor) on Apr 20, 2004 at 17:49 UTC
    This code seems to do what you want
    my $foo = 'foofoofoo'; print map {s/foo/bar/g; $_} ($foo);
    Update: No it doesn't, as $foo is still modified. I don't see a way.

    -Mark

Re: Non-destructive string substitution
by sgifford (Prior) on Apr 20, 2004 at 18:08 UTC
    You could write a sub that would create the temp variable for you and return it:
    #!/usr/bin/perl -w use strict; foreach my $str (qw(fish apple license gradual canaan)) { print sr($str,'s/([aeiou])/\u$1/g'),"\n"; } sub sr { my($in,$re)=@_; eval "\$in =~ $re;"; $@ and die "Bad regex '$re'\n"; $in; }
    although a temp variable is still being created, just not explicitly.

    This is probably the best you're going to be able to do (although there's probably a way to do it without eval), since doing something nondestructively means that the original must be kept intact, so there have to be two copies of the variable.

Re: Non-destructive string substitution
by EdwardG (Vicar) on Apr 20, 2004 at 18:59 UTC

    You might need to wait until Perl 6 for this functionality.

    This fortnight on Perl 6, week ending 2003-11-23
    I have the feeling that what's actually going to happen is that we'll end up with a supplementary, non destructive form of s/// which doesn't alter the original string, but returns a new string with the substitutions made.
Re: Non-destructive string substitution
by ysth (Canon) on Apr 20, 2004 at 20:18 UTC
    $ perl -we'$foo = "abcdefghij"; ($bar) = grep s/[ei]/x/g, "$foo"; prin +t $bar' abcdxfghxj
    (If I ever get my act together, I hope to implement $bar = $foo =~ s/[ei]/x/gr, but that will modify $foo as well as returning it as $bar.)

      this thread is old, I know, but I did the test and $foo is not modified, here the code i wrote

      my $s = "hi, how are you?"; my $s2 = ($s =~ s/,.*//gr); print "$s2 \n$s\n";
      __OUTPUT__ hi hi, how are you?
      hop this helps :)