in reply to Re (tilly) 2: Calling subroutine in a package from other perl sub.
in thread Calling subroutine in a package from other perl sub.

I never quite understood why this is considered dangerous style. This is the old style of calling subroutines. If you do not predeclare you subs then you either have to &sub or sub() to not get a bareword error with use strict

When you call a subroutine like &sub I understand that you are passing the value of @_ to the sub or the equivalent of sub(@_). If you call sub(), you instead are passing a null list. But exactly how is passing @_ dangerous if the sub is not using any passed variables?

I think not knowing the following is more dangerous.

#!/usr/bin/perl -w use strict; my @test = ("one","two","three"); my $x = "one"; my $y = "two"; my $z = "three"; test1($x,$y,$z); print "$x\n$y\n$z\n\n"; test2($x,$y,$z); print "$x\n$y\n$z\n"; test1(@test); print join "\n", @test, "\n"; test2(@test); print join "\n", @test, "\n"; sub test1 { my $x1 = shift; my $y1 = shift; my $z1 = shift; $_[0] = "test1: 1"; $_[1] = "test1: 2"; $_[2] = "test1: 3"; } sub test2 { my ($x2,$y2,$z2) = @_; $_[0] = "test2: 1"; $_[1] = "test2: 2"; $_[2] = "test2: 3"; }
Output:
one two three test2: 1 test2: 2 test2: 3 one two three test2: 1 test2: 2 test2: 3

Someone mind explaining this? In more detail than " '@_' is a local array, but its elements are aliases for the actual scalar paramters." What really gets me is how useing shift changes the behavior! Why?

zzSPECTREz

Replies are listed 'Best First'.
Re (tilly) 4: Calling subroutine in a package from other perl sub.
by tilly (Archbishop) on Jan 17, 2001 at 07:48 UTC
    First of all it is dangerous because the possibility for confusion is high. Sure, you may know to do it only when the called subroutine is unwilling to take arguments, but will the person who learns from your code know that?

    As for your cute test function, it is really simple. Perl scalars are always references to data, not the actual data. So if you want to convert a list of scalars into a list, it is substantially more efficient to just make a list of pointers to the data than really copy. And that is what Perl does when passing data into a function. However when you assign in Perl you assign by value, not reference.

    So your 3 variables coming in are really passed in by reference, so in test2 you are able to change the original variables (whether the originals were from a scalar or an array) through the references. (ie @_ is just a list of pointers back to the original variables.) In test1 you threw away the three references and then assigned to new variables. Well if you no longer have the three variables, then assigning to new ones won't change a thing.

    And as you note this works just as well if you have the original argument list being 3 variables or an array with 3 things.

    So the rule is that assignment is by value, and arguments are passed into functions by reference. You probably understand this correctly if you can puzzle out the following example:

    ($x, $y, $z) = 'x'..'z'; print "Original values.\n"; show_vals(); print "This rotates them.\n"; rotate($x, $y, $z); show_vals(); print "Why doesn't this?\n"; rotate_not($x, $y, $z); show_vals(); sub rotate { @_[-1..($#_-1)] = @_; } sub rotate_not { @_ = @_[-1..($#_-1)]; } sub show_vals { print "\$x: $x\n\$y: $y\n\$z: $z\n\n"; }

      Well, I would think it is because in sub rotate, you are assingning the data in @_ (x,y,z) to the aliases (references $x,$y,$z) indexed @_[-1..($#_-1]. However, in rotate_not, you are assinging the data (array slice) from @_[-1..($#_-1] to the array @_ and not the references. So you are overwriting the references to $x,$y,$z and not updating them.

      zzSPECTREz

        Exactly, assigning to a list means assigning to each element in that list. Assigning to an array means throwing away the existing array (if there is one) and creating a new one. Once again, Arrays are not lists. :-)
(tye)Re: Calling subroutine in a package from other perl sub.
by tye (Sage) on Jan 17, 2001 at 10:11 UTC

    A bit of a different explanation...

    Let's pretend we are in Perl4 so there are no \ and -> operators to deal with references so we can use them to talk about how Perl itself deals with aliases.

    Then calling test($x,$y,$z) sets @_ to (\$x,\$y,\$z). So if you do $_[0]= "new val" without having modified @_ then you end up doing ${\$x}= "new val" because Perl will see that $_[0] currently contains a reference to (alias of) $x and so modifying $_[0] also modifies $x.

    Well if you do my $x1= shift then you are left with @_ containing (\$y,\$z). Do that two more times and @_ is empty. So $_[0]= "new val" ends up putting a value into @_ where no value was before. So there is no reference (alias) for Perl to implicitly dereference so, of course, $x is not changed in that case. So you end up with @_ having ("new val") or ("x2","y2","z2") and @_ just gets thrown away in the end.

    Also, when @_ is empty, doing $_[0]= "x" finds $_[0] not being an alias to anything and so creates a new scalar and sets $_[0] to be an alias to it (the only alias at that point).

    I hope that helps some.

    Update: And calling test(@v) sets @_ to (\$v[0],\$v[1],\$v[2]) (if @v has three elements) (which could be written as \(@v) in Perl5). But doing @_= @v copies the values rather than making aliases.

            - tye (but my friends call me "Tye")

      Yes. This is a good explanation of the diferences between shift and accessing @_ directly. After I shift, the alias is gone from @_. So its obvious I cant modify the original value.

      However, things dont exactly work like this though. And an alias is not the same as a reference. Correct? Because if test($x,$y,$z) does @_ = (\$x, \$y, \$z) then in then in the code you would have to dereference the variable after you shift them to access them. They would be references not scalar data. my $x = shift @_; print $$x;

      zzSPECTREz

      NOTE: I appreciate your responses. You allways get me thinking.

        Yes, exactly!

        After some thought, I need better (or just more) terminology.

        Let us say that you use the \ and -> operators with "Perl references". Then let us say that Perl itself makes heavy use of a type of reference internally but we'll call these internal references "pointers" since that is probably less confusing for this discussion. Now a "pointer" isn't what a C programmer would call a pointer since there is at least one more layer of abstraction that isn't important for this discussion (so a "pointer" contains a C pointer plus a few other things).

        So a Perl scalar variable is really a name for a "pointer" to a scalar value (called an SV in the source code). So $x points to some SV structure that contains $x's flags saying what type(s) of value(s) $x currently has and contains those values.

        And a Perl array is an array of "pointers" to SVs. So calling test(@v) copies the "pointers" from @v to @_ so we end up with $v[0] and $_[0] pointing to the same SV structure and changing a value via one also changes the value seen via the other. The two are said to be aliases of each other.

        But doing @_= @v causes new SV structs to be allocated for @_ and the values from @v's SVs are copied to @_'s new SVs.

        And so doing "print $x" involves an "automatic dereference" of $x's "pointer".

        Now a "Perl reference" is a(n) SV that contains a "pointer".

        You can watch aliases in work via:

        sub refd { print join( " ", \(@_) ), $/; } my @v= qw( a b c ); print "@v: ", join( " ", \(@v) ), $/; refd( @v ); refd( $v[0] ); refd( @v[1,2,0] ); refd( "$v[1]" );
        You'll note that you get the same references except in the last case.

                - tye (but my friends call me "Tye")