An array and a variable as parameters in subroutine in Perl - perl

Consider:
sub abc()
{
}
abc(#array, $a);
How do I access #array and $a in subroutine abc()?
I know about $_[0] and $_[1], but I wasn't sure if I can use it for arrays.

You access a sub's arguments with the #_ array. The first argument is $_[0], the second - $_[1], etc. In this particular case, your array will be unrolled to list of its elements, so $_[0] is $array[0], $_[1] is $array[1] and then after all those elements, last element of #_ will be the value of $a.
If you want to avoid unrolling that always happens when you use an array in a list context, use a reference to the array instead. References to arrays and hashes are created using \. So call your function like:
abc(\#array, $a);
After that, $_[0] will have reference to #array and $_[1] will be $a. To access array elements through reference, use -> operator. $_[0]->[2] is same as $array[2]. Actually you can even drop -> as long as it is between brackets, so $_[0][2] will work too. See more details on references in perlref.

You have two options:
Pass the scalar variable first (the dirty way)
abc($a, #array);
Then receive the parameters in subroutine as
my ($a, #array) = #_;
Pass your array as reference by adding a backslash before the array variable (recommended)
abc(\#array, $a);
Then receive the parameters in subroutine as
my ($array_ref, $a) = #_;
And dereference the $array_ref
my #array = #$array_ref;
More information about perlref.

The other answers explained the two basic approaches. However, it is important to note that there is a big difference between the two: When you pass an array by reference, any changes you make to it also change the original array. Here is an example:
use warnings;
use strict;
my #array = (1, 2, 3, 4, 5);
sub by_ref
{
my $array_ref = $_[0];
#$array_ref = (0, 0, 0);
print "Array inside by_ref: #$array_ref\n";
}
sub by_val
{
my #array_copy = #_;
#array_copy = (0,0,0);
print "Array inside by_val: #array_copy\n";
}
by_val(#array);
print "Original array after calling by_val: #array\n";
by_ref(\#array);
print "Original array after calling by_ref: #array\n";
If you do pass by reference, you need to keep this behavior in mind, making a copy of the referenced array if you don't want changes made in your sub to affect the original.

It would be nice if you pass the array reference instead of an array as mentioned by Oleg V. Volkov like
sub abc()
{
my ( $array, $a ) = #_; #receiving the paramters
my #arr = #{$array}; # dereferencing the array
}
abc(\#array,$a);

Related

Perl: Sub's arguments explain

In perl all sub's argument writting to #_ array, like this:
call_any_sub($a,$b,$c);
sub call_any_sub {
my $s_a = shift;
my $s_b = shift;
my $s_c = shift;
}
But, if i want to passed array as an argument to sub, i should use:
call_any_sub(#data_array);
sub call_any_sub {
my #data = #_;
}
Instead of similar:
call_any_sub(#data_array);
sub call_any_sub {
my #data = shift;
}
So, why #data_array replaces the array of arguments and not written in it (as expected)?
One can only pass a list of scalars to a subroutine (and that's all they can return). After all, the arguments are presented to the sub as an array (#_), and arrays can only contains scalars.
You can either (inefficiently) recreate the array in the sub
sub foo {
my #bars = #_;
say for #bars;
}
foo(#bars);
or you can pass a reference to the array
sub foo {
my ($bars) = #_;
say for #$bars;
}
foo(\#bars);
You need to understand what shift does.
The shift/unshift pair of commands are parallel to the pop/push pair of commands. All of these commands operate on arrays. By default, shift (and only shift) assumes the #_ array when called in a subroutine and #ARGV when called in the main program. This means the following two statements are identical in a subroutine:
my $foo = shift #_; # Explicit Argument
my $foo = shift # Implicit Argument
Perl's parameter passing is an interesting concept because it doesn't really do named parameter passing like almost all other programs. Instead, everything is passed as one long list of scalars. This makes it hard when you aren't passing in a scalar.
It works okay if I am only passing in a single hash or array:
munge_hash ( %foo );
sub munge_hash {
my %hash = #_;
...
}
And, you have to be careful if you're passing in multiple arguments and an array. In this case, the array must be the last in your list of arguments:
my $foo = "floop";
my $bar = "bloop";
my #array = qw(loop coop soop);
munge_this ( $foo, $bar, #array );
sub munge_this {
say join ":", #_; # Prints "floop:bloop:loop:coop:soop"
my $var1 = shift # floop
my $var2 = shift # bloop
my #arry = #_ # The rest is the array passed.
However, things really fall apart if you're passing in multiple arrays or hashes. All of the elements get merged into a single list of scalars represented by #_.
munge_two_arrays ( #foo, #bar );
sub munge_two_arrays {
# Problem! Elements of both arrays are in #_.
# How do I separate them out?
}
Thus, it is common not to pass in a whole array, but an array reference:
munge_two_arrays( \#foo, \#bar ); # These are array references
sub munge_two_arrays {
my $array1_ref = shift;
my $array2_ref = shift;
my #array1 = #{ $array1_ref } # Dereference array references to make arrays
my #array2 = #{ $array2_ref } # Dereference array references to make arrays
}
This keeps the values of the two arrays from getting merged into a single #_.

In Perl, why am I getting a warning when I use #_[0], #_[1], etc to access arguments to my subroutine?

The below one the code.
sub max
{
if (#_[0] > #_[1])
{
#_[0];
}
else
{
#_[1];
}
}
print "biggest is ".&max(37,25);
When I ran it, I got the following warnings,
Scalar values #_[0] better written as $_[0] at file.pl line 3.
Scalar values #_[1] better written as $_[1] at file.pl line 3.
Scalar values #_[0] better written as $_[0] at file.pl line 5.
Scalar values #_[0] better written as $_[0] at file.pl line 9.
biggest is 37.
Although I got a correct output, but I wonder what could be the reason behind this warning, Since I think that using #_ in a subroutine would be apropriate than $_.
The problem is that you are referring to your single array element by using an array slice instead of a scalar. Just like the error says. An array slice is a list of elements from an array, for example:
my #a = (0 .. 9);
print #a[0,3,4]; # prints 034
Conversely, when you refer to a single array element you use the scalar prefix $:
print $a[3]; # prints 3
So when you do
#_[0];
Perl is telling you that the proper way to refer to a scalar value is by not using an array slice, but rather to use the scalar notation:
$_[0];
That is all.
Try to understand it with this example:
#array = (1,2,3); #array is the name of the array and # means that it's an array
print $array[1];
#this will print element at index 1 and you're doing it in scalar context
Similarly,
#_ = (1,2,3); #_ is the name of the array
print $_[1];
#this will print element at index 1 of array _ and again you're doing it in scalar context
You are referring to an array, instead of a scalar. #_[0] means ($_[0]). But perl is kind of clever so it warns You that instead of an explicit single element list You should return a scalar. Here You should use $_[0].
I suggest to use prototype, as now You could call max (1, 2, 3) and the result will be 2, instead of 3. So define as
sub max ($$) { $_[0] > $_[1]) ? $_[0] : $_[1] }
Or better, You can use for undefined number (>=2) of elements. Maybe pointless to call it with 0 or 1 items.
sub max (#) {
return undef if $#_<0;
my $s = shift;
for(#_) { $s = $_ if $_ > $s } $s
}

Why pass by ref needs shift instead of #_?

I have a question regarding pass by reference to subroutines in Perl. For values if I pass using #_ it works but for ref only shift works . Not sure why. I have givedn sample code below:
This works:
#! /usr/bin/perl
use strict;
use warnings;
my $name = 'John';
PassScalarByRef( \$name );
sub PassScalarByRef{
my $got = shift;
print "Hello $$got\n";
}
but not this one:
#! /usr/bin/perl
use strict;
use warnings;
my $name = 'John';
PassScalarByRef( \$name );
sub PassScalarByRef{
my $got = #_;
print "Hello $$got\n";
}
In the second case, assigning to $got provides a scalar context to #_, which causes it to evaluate to its size (number of elements). You can instead say:
my ($got) = #_;
...to assign the first element of #_ to $got, as you expect.
You are using the #_ array in scalar context. $got now contains the number of arguments passed. You should try my ($got) = #_, which now uses the array in list context which is what you mean.
Most operators give their operands a specific context in a consistent way; for instance, + gives both its operands scalar context; || gives its left operand scalar context and its right operand whatever context the || itself has.
Assignment is a little different, because there are two types, list assignment and scalar assignment.
Scalar assignments look like:
$scalar = ...
lvaluesub() = ...
(lvalue subs are a little-used feature of perl; the builtin pos is an example).
Only one value is being assigned, and these give ='s right operand scalar context.
List assignments look like this:
#array = ...
#arraytoslice[...] = ...
%hash = ...
#hashtoslice{...} = ...
( ... ) = ...
or even
() = ...
All these expect a list of values to assign, so give the right operand list context.
When you say:
my $got = #_;
this is a scalar assignment, and so #_ gets scalar context, which causes it to return its number of elements, not the first value.
Instead, say:
my ($got) = #_;
Some people do this consistently, even for subs with only one operand; others do
my $param1 = shift;
my $param2 = shift;
for subs with a small number of operands.
It's common for methods to get the object/class using shift and a list assignment from #_ for the remaining parameters.
Used differently.
my $got = $_[0];

Perl: Special array #_ is not really an alias?

The special array, #_ , where all the arguments passed to a function are present, is actually an alias to the arguments passed. Hence, any change we make directly to this special array #_ will reflect in the main as well. This is clear.
#!/usr/bin/perl
use warnings;
use strict;
$\="\n";
sub func {
print \#_;
$_++ for(#_);
}
my #arr=(2..4);
print \#arr;
func(#arr);
print "#arr";
For the above program, I expected the reference of #arr and #_ to point to the same location since it is an alias. But it is not so.
On running the above:
ARRAY(0x1b644d0)
ARRAY(0x1b644e0)
3 4 5
If they are pointing to 2 different locations, how the changes done in #_ is reflecting in #arr?
Am I seeing something wrong? Please advice.
This might answer you question:
use warnings;
use strict;
$\="\n";
sub func {
print \#_;
$_++ for(#_);
print \$_ for #_;
}
my #arr=(2..4);
print \#arr;
func(#arr);
print "#arr";
print \$_ for #arr;
Output
ARRAY(0x17fcba0)
ARRAY(0x1824288)
SCALAR(0x17fcc48)
SCALAR(0x18196f8)
SCALAR(0x1819710)
3 4 5
SCALAR(0x17fcc48)
SCALAR(0x18196f8)
SCALAR(0x1819710)
As you see, individual arguments have the same address but the container is not the same. If you push an item to #_ in func the #arr will not change (so you can do shift in funct). So, each argument is an alias and array elements are passed as individual items. #_ contains all items passed into the subroutine. If you want to modify an array argument you need to pass it by reference.
#_ isn't aliased; its elements are.
Remember that
func(#arr);
is the same as
func($arr[0], $arr[1], ...);
because the only thing that can be passed to a sub is a list of scalars, and an array evaluates to a list of its elements in list context.
So that means
func(#arr);
is basically the same as
local #_;
alias $_[0] = $arr[0];
alias $_[1] = $arr[1];
...
&func;
Changing the elements of #_ will change elements of #arr, but adding and removing elements of #_ won't change #arr since they are different arrays.
>perl -E"#a=(4..6); sub { $_[0] = '!'; say #_; }->(#a); say #a;"
!56
!56
>perl -E"#a=(4..6); sub { splice(#_,0,1,'!'); say #_; }->(#a); say #a;"
!56
456

Dereferencing a list reference in hash element

Can someone finish this for me and explain what you did?
my %hash;
#$hash{list_ref}=[1,2,3];
#my #array=#{$hash{list_ref}};
$hash{list_ref}=\[1,2,3];
my #array=???
print "Two is $array[1]";
#array = #{${$hash{list_ref}}};
(1,2,3) is a list.
[1,2,3] is a reference to a list an array (technically, there's no such thing in Perl as a reference to a list).
\[1,2,3] is a reference to a reference to an array.
$hash{list_ref} is a reference to a reference to an array.
${$hash{list_ref}} is a reference to an array.
#{${$hash{list_ref}}} is an array.
Since a reference is considered a scalar, a reference to a reference is a scalar reference, and the scalar dereferencing operator ${...} is used in the middle step.
Others have pretty much already answered the question, but more generally, if you are ever confused about a data structure, use Data::Dumper. This will print out the structure of the mysterious blob of data, and help you parse it.
use strict; #Always, always, always
use warnings; #Always, always, always
use feature qw(say); #Nicer than 'print'
use Data::Dumper; #Calling in the big guns!
my $data_something = \[1,2,3];
say Dumper $data_something;
say Dumper ${ $data_something };
Let's see what it prints out...
$ test.pl
$VAR1 = \[
1,
2,
3
];
$VAR1 = [
1,
2,
3
];
From the first dump, it appears that $data_something is a plain scalar reference to an array reference. That lead me to add the second Dumper after I ran the program the first time. That showed me that ${ $data_something } is now a reference to an array.
I can now access that array like this:
use strict; #Always, always, always
use warnings; #Always, always, always
use feature qw(say); #Nicer than 'print'
use Data::Dumper; #Calling in the big guns!
my $data_something = \[1,2,3];
# Double dereference
my #array = #{ ${ $data_something } }; #Could be written as #$$data_something
for my $element (#array) {
say "Element is $element";
}
And now...
$ test.pl
Element is 1
Element is 2
Element is 3
It looks like you meant:
my $hash{list_ref} = [1,2,3];
and not:
$hash{list_ref} = \[1,2,3];
That latter one got you an scalar reference of a array reference which really doesn't do you all that much good except add confusion to the situation.
Then, all you had to do to refer to a particular element is $hash{list_ref}->[0]. This is just a shortcut for ${ $hash{list_ref} }[0]. It's easier to read and understand.
use strict;
use warnings;
use feature qw(say);
my %hash;
$hash{list_ref} = [1, 2, 3];
foreach my $element (0..2) {
say "Element is " . $hash{list_ref}->[$element];
}
And...
$ test.pl
Element is 1
Element is 2
Element is 3
So, next time you are confused about what a particular data structure looks like (and it happens to the best of us. Well... not the best of us, It happens to me), use Data::Dumper.
my %hash;
#$hash{list_ref}=[1,2,3];
#Putting the list in square brackets makes it a reference so you don't need '\'
$hash{list_ref}=[1,2,3];
#If you want to use a '\' to make a reference it is done like this:
# $something = \(1,2,3); # A reference to a list
#
# or (as I did above)...
#
# $something = [1,2,3]; # Returns a list reference
#
# They are the same (for all intent and purpose)
print "Two is $hash{list_ref}->[1]\n";
# To make it completely referenced do this:
#
# $hash = {};
# $hash->{list_ref} = [1,2,3];
#
# print $hash->{list_ref}[1] . "\n";
To get at the array (as an array or list) do this:
my #array = #{ $hash{list_ref} }
[ EXPR ]
creates an anonymous array, assigns the value returned by EXPR to it, and returns a reference to it. That means it's virtually the same as
do { my #anon = ( EXPR ); \#anon }
That means that
\[ EXPR ]
is virtually the same as
do { my #anon = ( EXPR ); \\#anon }
It's not something one normally sees.
Put differently,
1,2,3 returns a list of three elements (in list context).
(1,2,3) same as previous. Parens simply affect precedence.
[1,2,3] returns a reference to an array containing three elements.
\[1,2,3] returns a reference to a reference to an array containing three elements.
In practice:
my #data = (1,2,3);
print #data;
my $data = [1,2,3]; $hash{list_ref} = [1,2,3];
print #{ $data }; print #{ $hash{list_ref} };
my $data = \[1,2,3]; $hash{list_ref} = \[1,2,3];
print #{ ${ $data } }; print #{ ${ $hash{list_ref} } };