user785099
user785099

Reputation: 5553

three questions on an existing perl subroutine

I am trying to use the following perl function, but I am not very clear about three points:

sub inittwiddle { 
    my ($m,$n,$aref) = @_; 
    my $i; 
    $$aref[0] = $n+1; 
    for ($i=1; $i != $n-$m+1; $i++) { 
       $$aref[$i] = 0; 
    } 
    while($i != $n+1) { 
       $$aref[$i] = $i+$m-$n; 
       $i++; 
    } 
    $$aref[$n+1] = -2; 
    $$aref[1] = 1 if ( $m == 0 ); 
} 

First, what does my ($m,$n,$aref) = @_; stand for?

Second, how to understand the $$ for sth like $$aref[0] = $n+1;

And this function was invoked as inittwiddle($M,$N,\@p); what does \@p stand for?

Upvotes: 3

Views: 439

Answers (3)

David W.
David W.

Reputation: 107040

First, what does my ($m,$n,$aref) = @_; stand for?

When you pass a set of arguments into a subroutine, they're passed as a list (sometimes called an array) to a special array called @_.

In this case, the subroutine is setting the variables $m, $n, and $aref to the first three values in this special array.

It is similar to this:

my $m = @_[0];
my $n = @_[1];
my $aref = @_[2];

Second, how to understand the $$ for sth like $$aref[0] = $n+1;

Now, we're getting into references. A reference is a pointer to another Perl variable. Imagine I have an array like this:

@my_list = ("one", "two", "three", "four");

I can create a pointer to this list by doing this:

my $my_list_ref = \@my_list;

The variable $my_list_ref now points to @my_list. The backslash before the variable @my_list tells Perl that I don't want to make $my_list_ref equal to @my_list. Instead, I merely want $my_list_ref to point to @my_list.

Now, if I want to actually refer to the data that $my_list_ref points to, I do what is called dereferencing it. I do that by putting a @ in front of it:

print join ",", @{ $my_list_ref };   #prints "one, two, three, four"

And if I want to refer to a particular value in the list that $my_list_ref points to, I can do the same type of syntax:

print ${ $my_list_ref }[0];   #Prints 'one'
print ${ $my_list_ref }[1];   #Prints 'two'

Putting the reference variable in curly braces helps clarify what you're doing, but at the same time, it can make it harder to parse. To solve this issue, Perl allows you to do some syntax simplification.

In simple cases, you can remove the curly braces:

print $$my_list_ref[0];   #Prints 'one'
print $$my_list_ref[1];   #Prints 'two'

Note the double dollar sign! This is what you see in your subroutine.

Most of the time you'll see the -> syntax, which is the preferred coding style:

print $my_list_ref->[0];  #Prints 'one'
print $my_list_ref->[1];  #Prints 'two'

And this function was invoked as inittwiddle($M,$N,\@p); what does \@p stand for?

Now, we get into the question why you would use references in the first place. First, let's take a look at some of the limitations of Perl subroutines:

  1. All parameters are passed in a single @_ array. What if I have a subroutine that takes two lists as parameters? They both get munched into the single @_ array.
  2. If I am passing in a really, really big list into my subroutine, that could take a lot memory as that list is copied into the @_ array.
  3. In a Perl subroutine, all of the variables I pass have independent identities in the subroutine. Changing them in the subroutine doesn't change their values once they leave the subroutine. What if I want to change their values?

Using a reference solves all of these problems. The developer who wrote this subroutine was trying to solve for both problem #2 and problem #3. The call:

inittwiddle($M, $N, \@p);

Is taking two scalar variables, and a reference to the array @p. If array @p had a million values in it, it could take a long time to copy over the entire list to the subroutine. Plus, the developer looks like they're also changing the items in @p. When the developer says:

$$aref[0] = $n + 1;

He's actually changing $p[0] to $n + 1. When the subroutine returns, $p[0] will have a different value than before the subroutine. Remember that @$aref isn't just a duplicate of @p, it points to @p, so it is @p.

You should read the Perl Reference Tutorial for more information about references.

Upvotes: 2

CanSpice
CanSpice

Reputation: 35788

  1. @_ is the list of arguments passed to the function. By doing my ($m,$n,$aref) = @_; you're assigning $_[0] to $m, $_[1] to $n, and $_[2] to $aref.

  2. $aref is a scalar value holding a reference to an array. To refer to elements within the array you can either access them via $aref->[0] (this is more idiomatic), or by dereferencing the array reference. By adding a @ to the front, you'd refer to the array (i.e. @$aref). However, you want the first element in the array, which is a scalar, so it's obtained by $$aref[0]. Adding brackets (${$aref}[0]) or using the arrow notation ($aref->[0]) clarifies this a little.

  3. \@p is a reference to the array @p. Since your function takes a scalar as the third argument, you have to pass in a scalar. \@p is such. When you pass an array reference into a function like this, it's important to note that any changes to the array (such as doing $$aref[0] = $n+1) are changes to the original array. If you want to avoid this, you would dereference the array into a temporary one, possibly by doing my @tmparr = @$aref; within the function.

Upvotes: 8

zigdon
zigdon

Reputation: 15063

The first question is just argument passing. When a perl sub is called, the arguments to it are in the special array @_. The first line just takes the first 3 items in the array, and puts them in those 3 local variables.

The other two questions involve references. I recommend taking a look at the perlref manpage. \@p is passing a reference the array @p. Then, that reference is put in $aref, and then the first element of the array is accessed with $$aref[0] (which might possibly be clearer to read as ${$aref}[0]).

Upvotes: 1

Related Questions