Why pass by ref needs shift instead of #_? - perl

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];

Related

run a subroutine by using argument

My code:
my $aaa = "abc";
sub myp{
print "$_";
}
myp($aaa);
I hope myp can print the argument it get.
But it said
Use of uninitialized value $_ in string at ./arg line 17.
The arguments to a subroutine in Perl are passed in the #_ array. This is not the same as the $_ variable.
A common idiom is to "unpack" these arguments in the first line of a function, e.g.
sub example {
my ($arg1, $arg2) = #_;
print "$arg1 and $arg2";
}
It's also possible to refer to arguments directly as elements of #_, e.g. as $_[0], but this is much harder to read and as such is best avoided.
I usually do something like:
my $first_arg = shift #_;
my $second_arg = shift #_;
You can also use the method of the other response:
my ($first_arg, $second_arg) = #_;
But be careful saying:
my $first_arg = #_;
Since you will get the number of arguments passed to the subroutine.
When you refer to $_ you are referencing the default string variable, you probably want in this case to refer #_, if you want to get a specific argument, you must say $_[narg], be also careful passing arrays to subroutines if you do:
some_sub(#myarray);
You will pass the entire array as it was the argument list, instead you should say:
some_sub(\#myarray);

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

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?
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).
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.
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.

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 #_.

An array and a variable as parameters in subroutine in 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);

implicit-conversion in perl

I am new to Perl, can anyone explain the following scripts for me please:
#!/usr/bin/env perl
use strict;
use warnings;
sub f1($) { my ($v) = #_; print "f1 $v\n"; }
sub f2(#) { my ($v) = #_; print "f2 $v\n"; }
my $s = "ww";
my #a = ("xx", "yy", "zz");
f1 $s; f1 #a; f2 $s; f2 #a;
The output in my computer is :
f1 ww
f1 3
f2 ww
f2 xx # why!!
Can anyone explain why the fourth output is xx? I thought it should be zz, since when array converts to scalar, it should be the last element of array.
No, with a statement such as:
my ($v, $foo, $bar) = #_;
$v will be assigned the first value in the #_ array, $foo the second, and so on. This is because the parentheses impose a list context. Any excess values will be ignored, unless one of your variables is an array, in which case it will slurp all remaining values.
my ($v, #foo, $bar) = #_; # wrong! $bar will never get any value
$v will get the first value, #foo all the rest. $bar will be undefined.
You may be thinking of assignment using a list:
my $v = qw(a b c);
But this is wrong, and will cause an error:
Useless use of a constant (a) in void context at -e line 1.
Useless use of a constant (b) in void context at -e line 1.
This is because the LHS using scalar context, it will (more or less) be similar to:
'a';
'b';
my $v = 'c';
You may notice that if we impose list context by putting $v inside parentheses, we get a different result:
my ($v) = qw(a b c); # $v is now 'a'
ETA: Regarding prototypes:
In f1, what you see is that the array is forced into scalar context because the subroutine expects a scalar argument. That is why f1 with an array prints 3 (the size). When the prototype looks for an array, the array remains in default list context, and the assignment is done as per normal (as described above).
As an extra note: prototypes have a very specific use, to make subroutines behave more like certain built-ins with regard to argument handling. Such as sort { code here } or push #array, $foo.
If this is not what you are after, you should skip prototypes all together and simply write:
sub f1 {
...
}
Documentation here
Perl array behaves in 2 differents flavours, regarding context:
Scalar context:
my $a = #tab; # get the array length
Array context:
my #newTab = #tab; # newTab is a copy of tab
Which can be reworded like this:
# 3 scalars in an array context (see parens) gets the contents of the tab
my ($a,$b,$c) = #tab;
Here, since #tab can be wider than the number of scalars, these scalars are populated from the beginning of the tab (and not from the end). So your code:
my ($a) = #tab;
will echo the first element of the tab
An array can't be converted to a scalar. An array can be evaluated in scalar context, though.
An array evaluated in scalar context does not return the last element of the array. As you saw with f1, it returns the number of elements in the array.
$ perl -E'my #a = qw( xx yy zz ); say #a; say scalar(#a);'
xxyyzz
3
Neither array is being evaluated in scalar context. An argument list expression is evaluated in list context.
f(#a);
is the same as
f($a[0], $a[1], $a[2]);
The RHS of a list assignment in evaluated in list context.
my ($v) = #_;
is the same as
my ($v) = ($_[0], $_[1], $_[2]);
which is the same as
my ($v, undef, undef) = ($_[0], $_[1], $_[2]);