views:

233

answers:

4

I am having trouble with a function I wrote...

sub TemplateReplace
{
    my($regex, $replacement, $text) = @_;
    $text =~ s/($regex)/($replacement)/gs;
}

my $text = "This is a test.";
TemplateReplace("test", "banana", $text);

But it doesn't work. I thought arguments were sent by reference in Perl. Does the line my($regex, $replacement, $text) = @_; then copy them? How do I fix this?

+9  A: 
sub TemplateReplace
{
   my($regex, $replacement, $text) = @_;
   $text =~ s/($regex)/($replacement)/gs;
   return $text;
}

 my $text = "This is a test.";
 $text = TemplateReplace("test", "banana", $text);

There. That should work.

And yes, your my( ..) = @_ does copy the args. So if you're modifying a variable, you need to return it unless it's a global.

Adnan
+1 for a correct method, though Chas. Owens's answer helped me understand more.
rlbond
+4  A: 

It's the "assignment" part of the sub-routine that is making the copies of the data.

If you modify the @_ arguments directly, they work as you expect. It is however, not very readable. :-)

use strict;
umask(0);
$|=1;
my $debug = 0;

my $text = "This is a test.";

print "Before 1: [$text]\n";
TemplateReplace("test", "banana", $text);
print "After 1: [$text]\n";

print "Before 2: [$text]\n";
TemplateReplace2("test", "banana", $text);
print "After 2: [$text]\n";

sub TemplateReplace
   {
   my ($regex, $replacement, $text) = @_;    

   $text =~ s/($regex)/($replacement)/gs;
   }

sub TemplateReplace2
   {
   $_[2] =~ s/$_[0]/$_[1]/gs;
   }

returns:

Before 1: [This is a test.]
After 1: [This is a test.]
Before 2: [This is a test.]
After 2: [This is a banana.]

Ron

Ron Savage
+6  A: 

You are modifying a copy of the $text you passed in; this will have no effect on the original.

#!/usr/bin/perl

use strict;
use warnings;

my $text = "This is a test.";

template_replace(qr/test/, "bannana", $text);

print "$text\n";

sub template_replace {
    my $regex       = shift;
    my $replacement = shift;
    $_[0] =~ s/$regex/$replacement/gs;
}

The code above works because the elements of @_ are aliased to the variables passed in. But Adnan's answer is the more commonly done. Modifying arguments passed into functions is surprising behavior and makes things like template_replace(qr/foo/, "bar", "foo is foo") not work.

Chas. Owens
A: 

Here is a variation on how to do it, which is almost identical to your code with a slight difference.

use strict;
use warnings;


sub TemplateReplace {
    my($regex, $replacement, $text) = @_;
    $$text =~ s/($regex)/$replacement/gs;
}



my $text = "This  is a test."; 
TemplateReplace("test", "banana", \$text);
print $text;

This behavior is explicit instead of implicit. In practice, it works identically to Chas. Owens result, but uses scalar-refs instead of relying on understanding the behaviour of arrays.

This will make it more obvious to anybody reading your code that the function "TemplateReplace" is intentionally modifying $text.

Additionally, it will tell you you're using it wrong by squawking with :

Can't use string ("This  is a test.") as a SCALAR ref while "strict refs" in use at replace.pl line 9.

If you happen to forget the \ somewhere.

Kent Fredric