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

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
}

Related

How do I return an array and a hashref?

I want to make a subroutine that adds elements (keys with values) to a previously-defined hash. This subroutine is called in a loop, so the hash grows. I don’t want the returning hash to overwrite existing elements.
At the end, I would like to output the whole accumulated hash.
Right now it doesn’t print anything. The final hash looks empty, but that shouldn’t be.
I’ve tried it with hash references, but it does not really work. In a short form, my code looks as follows:
sub main{
my %hash;
%hash=("hello"=>1); # entry for testing
my $counter=0;
while($counter>5){
my(#var, $hash)=analyse($one, $two, \%hash);
print ref($hash);
# try to dereference the returning hash reference,
# but the error msg says: its not an reference ...
# in my file this is line 82
%hash=%{$hash};
$counter++;
}
# here trying to print the final hash
print "hash:", map { "$_ => $hash{$_}\n" } keys %hash;
}
sub analyse{
my $one=shift;
my $two=shift;
my %hash=%{shift #_};
my #array; # gets filled some where here and will be returned later
# adding elements to %hash here as in
$hash{"j"} = 2; #used for testing if it works
# test here whether the key already exists or
# otherwise add it to the hash
return (#array, \%hash);
}
but this doesn’t work at all: the subroutine analyse receives the hash, but its returned hash reference is empty or I don’t know. At the end nothing got printed.
First it said, it's not a reference, now it says:
Can't use an undefined value as a HASH reference
at C:/Users/workspace/Perl_projekt/Extractor.pm line 82.
Where is my mistake?
I am thankful for any advice.
Arrays get flattened in perl, so your hashref gets slurped into #var.
Try something like this:
my ($array_ref, $hash_ref) = analyze(...)
sub analyze {
...
return (\#array, \#hash);
}
If you pass the hash by reference (as you're doing), you needn't return it as a subroutine return value. Your manipulation of the hash in the subroutine will stick.
my %h = ( test0 => 0 );
foreach my $i ( 1..5 ) {
do_something($i, \%h);
}
print "$k = $v\n" while ( my ($k,$v) = each %h );
sub do_something {
my $num = shift;
my $hash = shift;
$hash->{"test${num}"} = $num; # note the use of the -> deference operator
}
Your use of the #array inside the subroutine will need a separate question :-)

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

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

Passing a scalar reference in Perl

I know that passing a scalar to a sub is actually passing the reference, but since I am new to perl I still did the following test:
#!/usr/bin/perl
$i = 2;
subr(\$i);
sub subr{
print $_[0]."\n";
print $$_[0]."\n";
}
I thought the first line is going to print an address and the second line is going to give be back the number, but the second one is a blank line. I was pointed by someone one else to do this: ${$_[0]} and it prints the number. But she didn't know the reason why without {} it is not working and why it is working with {}. So what has happened?
It's because your second print statement is equivalent to doing this...
my $x = $$_; print $x[0];
When what you want is
my $x = $_[0]; print $$x;
In other words, the de-referencing occurs before the array subscript is evaluated.
When you add those curl-wurlies, it tells perl how to interpret the expression as you want it; it will evaluate $_[0] first, and then de-reference to get the value.
It's an order-of-evaluation thing.
$$_[0] is evaluated as {$$_}[0]
This is the 0th element of the reference of the scalar variable $_. It's taking the reference first and then trying to find the 0th element of it.
${$_[0]}
This is a reference to the 0th element of the array #_. It's finding the 0th element first then taking a reference of that.
If you set use strict and use warnings at the top of your code you'll see plenty of warnings about undefined values from your first attempt.
$$_[0] is like $foo[0], only with $_ in place of the array name. This means $_ is treated as an array reference, and the expression doesn't involve the scalar reference $_[0] at all. $_->[0] is equivalent, using the alternate -> syntax. Syntax for dereferencing may seem arbitrary and hard to remember, but there is underlying sense and order; a very good presentation of it is at http://perlmonks.org/?node=References+quick+reference.
You don't have to pass a reference to $i. The notation $_[0] is an alias for $i when you invoke it as subr( $i ).
use strict;
use warnings;
use Test::More tests => 2;
sub subr{ $_[0]++ } # messing with exactly what was passed first
my $i=2;
is( $i, 2, q[$i == 2] );
subr($i);
is( $i, 3, q[$i == 3] );
Another example is this:
use strict;
use warnings;
use Test::More tests => 6;
use Test::Exception;
sub subr{ $_[0]++ }
my $i=2;
is( $i, 2, q[$i == 2] );
subr($i);
is( $i, 3, q[$i == 3] );
sub subr2 { $_[0] .= 'x'; }
dies_ok { subr2( 'lit' ); } 'subr2 *dies* trying to modify a literal';
lives_ok {
my $s = 'lit';
subr2( $s );
is( $s, 'litx', q[$s eq 'litx'] );
subr2(( my $s2 = 'lit' ));
is( $s2, 'litx', q[$s2 eq 'litx'] );
} 'subr2 lives with heap variables';
Output:
ok 1 - $i == 2
ok 2 - $i == 3
ok 3 - subr2 *dies* trying to modify a literal
ok 4 - $s eq 'litx'
ok 5 - $s2 eq 'litx'
ok 6 - subr2 lives with heap variables
1..6