views:

261

answers:

5

Is there a way to replace text with a regex inline, rather than taking the text from a variable and storing it in a variable?

I'm a perl beginner. I often find myself writing

my $foo = $bar;
$foo =~ s/regex/replacement/;
doStuff($foo)

where I'd really like to write

doStuff($bar->replace(s/regex/replacement/));

or the like, rather than using a temporary variable and three lines.

Is there a way to do this? Obviously when the regex is sufficiently complicated it makes sense to split it out so it can be better explained, but when it's just s/\s//g it feels wrong to clutter the code with additional variables.

+2  A: 

You can use a do { } block to avoid creating a temporary variable in the current scope:

doStuff( do {(my $foo = $bar) =~ s/regex/replacement/; $foo} );
eugene y
So apparently what I want isn't possible without either clutter or my own function. (It's easy enough to write that, but that makes it harder for others to scan -- they won't know the semantics of my function.)
Charles
The whole idea was to clarify the results. Doing what you're showing still uses a temporary variable `$foo`, and makes the program harder to understand.I believe what the poster really wanted to do was avoid having to create a temporary `$foo` to do put in the doStuff function.
David W.
A: 

Is this what you want?:

my $foo = 'Replace this with that';
(my $bar = $foo) =~ s/this/that/;
print "Foo: $foo\nBar: $bar\n";

Prints:

Foo: Replace this with that
Bar: Replace that with that
Mike
That saves a line by putting the definition together with the substitution, but still uses a temporary. Is it impossible to avoid this?
Charles
+4  A: 

You really can't do what you want because the substitution function returns either a 1 if it worked or an empty string if it didn't work. That means if you did this:

doStuff($foo =~ s/regex/replacement/);

The doStuff function would be using either 1 or an empty string as a parameter. There is no reason why the substitution function couldn't return the resultant string instead of just a 1 if it worked. However, it was a design decision from the earliest days of Perl. Otherwise, what would happen with this?

$foo = "widget";
if ($foo =~ s/red/blue/) {
    print "We only sell blue stuff and not red stuff!\n";
}

The resulting string is still widget, but the substitution actually failed. However, if the substitution returned the resulting string and not an empty string, the if would still be true.

Then, consider this case:

$bar = "FOO!";
if ($bar =~ s/FOO!//) {
   print "Fixed up \'\$bar\'!\n";
}

$bar is now an empty string. If the substitution returned the result, it would return an empty string. Yet, the substitution actually succeeded and I want to my if to be true.

In most languages, the substitution function returns the resulting string, and you'd have to do something like this:

if ($bar != replace("$bar", "/FOO!//")) {
   print "Fixed up \'\$bar''!\n";
}

So, because of a Perl design decision (basically to better mimic awk syntax), there's no easy way to do what you want. However you could have done this:

($foo = $bar) =~ s/regex/replacement/;
doStuff($foo);

That would do an in place setting of $foo without first assigning it the value of $bar. $bar would remain unchanged.

David W.
How disappointing. Thanks for the very complete answer.
Charles
Technically, `s///` returns the number of substitutions made (or the empty string if that's 0). But if you don't use `/g`, obviously that can't be more than 1.
cjm
Theoretically, you could have a modifier for `s///` that tells it to make a copy of the string, (possibly) modify the copy, and return that new string. I think I've seen that suggested before, but it hasn't (yet) made it into Perl, and it might never be added.
cjm
A: 

There is yet another way: Write your own function:

sub replace (
    my $variable = shift;
    my $substring = shift;

    eval "\$variable =~ s${substring};";
    return $variable
}

doStuff(replace($foo, "/regex/replace/"));

This wouldn't be worth it for a single call, and it would probably just make your code more confusing in that case. However, if you're doing this a dozen or so times, it might make more sense to write your own function to do this.

David W.
+4  A: 
use Algorithm::Loops "Filter";

# leaves $foo unchanged
doStuff( Filter { s/this/that/ } $foo );
ysth
`List::MoreUtils` has the same function called `apply` which is probably a better name for it. `Filter` makes me think of `grep` type functionality (probably because `grep` is called `filter` in most languages).
Eric Strom