Get the first item of the list returned by function - perl

A simple simulation of the problem:
use strict;
use warnings;
sub uniq
{
my %seen;
grep !$seen{$_}++, #_;
}
my #a = (1, 2, 3, 1, 2);
print shift #{uniq(#a)};
Can't use string ("3") as an ARRAY ref while "strict refs" in use

Need to impose a list context on the function call, and then pick the first element from the list.
The print, or any other subroutine call, already supplies a list context. Then one way to extract an element from a returned list
print +( func(#ary) )[0];
This disregards the rest of the list.
That + is necessary (try without it), unless we equip print itself with parentheses around all its arguments, that is
print( (func(#ary))[0] );

If uniq returned an array reference, then #{uniq(...)} would be the correct idiom to get an array (which is a suitable argument for shift). For a more general list, you can cast the list to an array reference and then dereference it.
print shift #{ [ uniq(#a) ] };

One option could be to return an array reference:
sub uniq {
my %seen;
[grep !$seen{$_}++, #_];
}

Related

Perl reference not printing the expected value

This is my program but why not it is printing my array values instead.
use strict;
use warnings;
use Data::Dumper;
my (#arr1,#arr2) = ([1,1,1,2,3,4],[5,5,5,6,9,87]);
my #arr3 = [\#arr1,\#arr2];
foreach (#arr3){
foreach (#$_){
print $_;
}
}
Output:
ARRAY(0x556414c6b908)ARRAY(0x556414c6b7e8)
but why not it is printing my array values instead.
Because the values are array references. To print the inner values, use dereference:
print #{ $array_ref };
For complex structures (arrays of arrays), you can use Data::Dumper:
use Data::Dumper;
print Dumper($array_ref);
But it still wouldn't work. You can't assign to several arrays at once. The first array gets all the values, the remaining arrays stay empty.
Documented in perlsub:
Do not, however, be tempted to do this:
(#a, #b) = upcase(#list1, #list2);
Like the flattened incoming parameter list, the return list is also
flattened on return. So all you have managed to do here is stored
everything in #a and made #b empty.
First of all, you weren't assigning anything to #arr2. You used something like the following to try to assign to #arr2:
(#arr1, #arr2) = ...;
However, Perl has no way to know how many scalars to assign to #arr1 and how many to assign to #arr2, so it assigns them all to #arr1. Use two different assignments instead.
Secondly, [ ] creates an array and returns a reference to it, so
my #arr1 = [1,1,1,2,3,4];
assigns a single scalar (a reference) to #arr1. This is what you are printing. You want
my #arr1 = (1,1,1,2,3,4);
Same goes for #arr2 and #arr3.
Therefore, your code should be
use strict;
use warnings;
use feature qw( say );
my #arr1 = (1,1,1,2,3,4);
my #arr2 = (5,5,5,6,9,87);
my #arr3 = (\#arr1,\#arr2);
for (#arr3) {
say join ", ", #$_;
}
or
use strict;
use warnings;
use feature qw( say );
my #arr3 = ([1,1,1,2,3,4],[5,5,5,6,9,87]);
for (#arr3) {
say join ", ", #$_;
}

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
}

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

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