If perl is call-by-reference why does this happen?

2019-08-25 08:13发布

问题:

I've read that perl uses call-by-reference when executing subrutines. I made a simple piece of code to check this property, but it behaves like if perl was call-by-value:

$x=50;
$y=70;

sub interchange {
    ($x1, $y1) = @_;

    $z1 = $x1;
    $x1 = $y1;
    $y1 = $z1;

    print "x1:$x1 y1:$y1\n";
}

&interchange ($x, $y);

print "x:$x y:$y\n";

This produces the following output:

$ perl example.pl
x1:70 y1:50
x:50 y:70

If arguments were treated in a call-by-reference way, shouldn't x be equal to x1 and y equal to y1?

回答1:

To modify the values outside of the sub, you would have to modify the values of @_.

The following sub interchange does modify the values:

sub interchange {
    ($x1, $y1) = @_; # this line copies the values to 2 new variables

    $z1 = $x1;
    $x1 = $y1;
    $y1 = $z1;

    $_[0] = $x1; # this line added to change value outside sub
    $_[1] = $y1; # this line added to change value outside sub

    print "x1:$x1 y1:$y1\n";
}

This gives the output:

x1:70 y1:50
x:70 y:50

More info here: http://www.cs.cf.ac.uk/Dave/PERL/node51.html

But, to quote the article:

You can see that the function was able to affect the @array variable in the main program. Generally, this is considered bad programming practice because it does not isolate what the function does from the rest of the program.



回答2:

Perl is always definitely call by reference. You're statement ($x1, $y1) = @_ is copying the original argument values, since @_ holds aliases to the original parameters.

From perlsub manpage:

Any arguments passed in show up in the array @_ . Therefore, if you called a function with two arguments, those would be stored in $[0] and $[1] . The array @_ is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element $_[0] is updated, the corresponding argument is updated (or an error occurs if it is not updatable).



回答3:

I'm just starting with Perl as well, and I believe you're misunderstanding just what you're passing to the subroutine. When you pass $x and $y you are passing the scalars $x and $y are set to. You need to explicitly pass a reference, which also happens to be a scalar (being the only thing are ever allowed to pass to subroutines). I understand where you're coming from in thinking things are call-by-reference since for arrays and hashes, since you need to pass references to those.

This code should do what you're looking for:

#!/usr/bin/perl

$x=50;
$y=70;

sub interchange {
    ($x1, $y1) = @_;

    $z1 = $$x1; # Dereferencing $x1
    $$x1 = $$y1; # Dereferencing $x1 and $y1
    $$y1 = $z1; # Dereferencing $y1

    print "x1:$$x1 y1:$$y1\n";
}

&interchange (\$x, \$y); # Passing references to $x and $y, not their values

print "x:$x y:$y\n";

I pass in references to $x and $y using \$x and \$y. Then, I use $$x and $$y to dereference them within the subroutine.