views:

195

answers:

5

I got a headache looking for this: How do you use s/// in an expression as opposed to an assignment. To clarify what I mean, I'm looking for a perl equivalent of python's re.sub(...) when used in the following context:

newstring = re.sub('ab', 'cd', oldstring)

The only way I know how to do this in perl so far is:

$oldstring =~ s/ab/cd/;
$newstring = $oldstring;

Note the extra assignment.

+10  A: 

You can use ($new = $old) =~ s/whatever/whateverelse/; for an exactly same functionality you are looking for:

use strict;
my $old = "OLD";
my $new;
($new = $old) =~ s/OLD/NEW/;
print "old=$old, new=$new";

Produces:

old=OLD, new=NEW

Exactly what you want

If you're looking for a function, you can just define your own to avoid assignment:

use strict;
sub re_sub { 
   my ($find, $replace, $old) = @_;
   my $new = $old;
   $new =~ s/$find/$replace/;
   return $new;
}

my $old = "ab";
my $new = re_sub('ab', 'cd', $old);
print "new=$new\n";

Results in new=cd.

DVK
+6  A: 

Perl's regular expression substitution always happens 'in-place'. So you need to copy the string to a new variable and operate on the new variable:

(my $newstring = $oldstring) =~ s/ab/cd/;
Grant McLean
Based on this, am I right to assume that you *cannot* have two s/// in one line, one which uses the result of the other, with no intermediate assignments?
mikeY
While you could achieve that by adding parentheses and =~ on the end, there's no clean syntax for it.
Grant McLean
+2  A: 

You want to have $newstring be the result of the substitution, right?

Something like this:

($newstring = $oldstring) =~ s/ab/cd;

Should work. The assignment sets $newstring to $oldstring and then evaluates to $newstring, which is what the substitution acts on.

jon
+7  A: 

You seem to have a misconception about how =~ works. =~ is a binding operator that associates a variable with a regexp operator. It does not do any assignment.

The regexp operators all work by default with the topic variable $_, so s/foo/bar/; is the same as $_ =~ s/foo/bar/;. No assignment occurs. The topic variable is transformed.

The case is analogous when operating on any other variable. $var =~ s/foo/bar/; transforms $var by replacing the first instance of foo with bar. No assignment occurs.

The best advice I can give you is to write Python in Python and Perl in Perl. Don't expect the two languages to be the same.

You could do like DVK suggests and write a subroutine that will reproduce the substitution behavior you are used to.

Or you could try some idiomatic Perl. Based on your expressed desire to apply multiple transformations in one line, I've provided a couple examples you might find useful.

Here I use a for loop over one item to topicalize $var and apply many hard-coded transformations:

for( $var ) {
    s/foo/bar/;
    s/fizz/buzz/;
    s/whop/bop-a-loo-bop/;
    s/parkay/butter/;
    s/cow/burger/;
}

Or maybe you need to apply a variable group of transforms. I define a subroutine to loop over a list of array references that define old/new transformation pairs. This example takes advantage of Perl's list oriented argument processing to handle any number of transformations.

my $foo = transform(
    'abcd' =>
    [ 'a',  'b'    ], 
    [ 'bb', 'c'    ],
    [ 'cc', 'd'    ],
    [ 'dd', 'DONE' ],
);

sub transform {
    my $var = shift;
    for (@_ ) {
        my ($old, $new) = @$_;
        $var =~ s/$old/$new/;
    }

    return $var;
}

Finally a bit of messing about to provide a version of transform that modifies its first argument:

my $foo = 'abcd';

transform_in_place(
    $foo =>
    [ 'a',  'b'    ], 
    [ 'bb', 'c'    ],
    [ 'cc', 'd'    ],
    [ 'dd', 'DONE' ],
);

print "$foo\n";

sub transform_in_place {
    for my $i (1..$#_ ) {
        my ($old, $new) = @{$_[$i]};
        $_[0] =~ s/$old/$new/;
    }
}

For my own project I'd probably use one of the first two options depending on the needs of the particular problem.

daotoad
+1  A: 

Based on this, am I right to assume that you cannot have two s/// in one line, one which uses the result of the other, with no intermediate assignments? – mikeY

Yes you are correct. If you want to apply multiple substitutions to the same string I would do

    $newstring = $oldstring ;
    $newstring  =~ s/ab/cd/ ;
    $newstring  =~ s/xx/yy/ ;

The following won't work because s// returns the number of substitutions made, not the changed string.

    $newstring = $oldstring) =~ s/ab/cd/ =~ s/xx/yy/ ;

In summary, Perl's regex operations are very different to Python's and you are better trying to learn what Perl does from scratch rather than trying to map Python concepts onto Perl

justintime