Referenced array to array yield size instead of actual array - perl

Probably a simple answer, but this has annoyed me for quite some time. I have a variable that looks to be a reference to an array of objects (hashes), i.e. the subroutine retrieving this array returns this
return \#my_entities;
All places I have looked states, in order to loop through these entities I need to do something like this
for my $obj (#{$ref}) {
#do stuff
}
Why is it then if I do as above: #{$ref}, I get SCALAR "2" if I have 2 elements instead of the array? It's like #{$ref} gives me the size rather than the array.
EDIT: To elaborate more, it appears the operators work opposite in my context, if I do
scalar $ref
I get the array. and if I do
#($ref)
I get the length. Is there some hidden option somewhere that reverses the behavior?
I have only recently started working with perl and the code base is rather huge so it would be impossible to paste it all in here, but the gist of the trouble spot looks very similar to one of the answers below. Basically a sub returns a reference to an array/list and that list is being attempted looped through.
If I do this
sub f {
my #my_entities = #_;
return \#my_entities;
}
my $ref = f(qw( a b c d ));
for my $obj (#{$ref}) {
print $obj;
}
I get 4 as a result, it doesn't loop through elements. If I change #{$ref} to scalar $ref I get the a b c d listed.
Should be said I am using the Camelcade debugger to run this on a linux server through Webstorm on my laptop.

If you evaluate #{$ref} in scalar context, you will get the number of elements in the array referenced by $ref (just as if you had evaluated #a in scalar context).
If you evaluate #{$ref} in list context, you will get the elements of the array referenced by $ref (just as if you had evaluated #a in list context).
In the code you posted, you are doing the latter.
use strict;
use warnings;
use feature qw( say );
sub f {
my #my_entities = #_;
return \#my_entities;
}
my $ref = f(qw( a b c d ));
for my $obj (#{$ref}) {
say $obj;
}
Output:
a
b
c
d

There is no difference here between array references and actual arrays. If you evaluate an array in list context, you get the array elements. If you evaluate an array in scalar context, you get the number of elements in the array.
my #array = qw[A B C];
print "#array"; # list context gives 'A B C'
print scalar #array; # scalar context gives 3
my $array_ref = \#array;
print "#$array_ref"; # list context gives 'A B C'
print scalar #$array_ref; # scalar context gives 3

Related

perl reference and dereference on return statement

I am learning perl, and have a problem related to references.
I am working on the function get_id.
If i return $self->{_id}, I will get two array address which cannot run (c).
In my understanding, $a is the reference and #{$a} is array and #{$a}[0] would return the value 0?
This is my first time post question on stackoverflow, therefore the explaination may not clear enough, sorry about that.
#a1 = [0]
#a2 = [1]
my self{
_id = [\#a1,\#a2]; } //given
sub get_id(){
my $self = shift;
return $self->{_id};
}
sub print{
...
my $a = $obj -> get_id();
my $b = #{$a}[0] * 100; // c (given)
..
}
When $arr is a reference to an array, #$arr or #{ $arr } is the array it refers to. The first (or zeroth) element of the array is $arr->[0] (or $$arr[0], but the arrow notation is more common).
The following
my #arr = [0];
creates an array with a single element, and that element is an array reference. To get the 0, you need to do
$arr[0]->[0]
which can be shortened to
$arr[0][0]
To create an array with 0 as the single element, use
my #arr = (0);
BTW, don't use $a and $b, they are special variables used in sort. Lexicalising them with my can lead to bugs that are hard to debug.

Why does perl only dereference the last index when the range operator is used?

I have an array, #array, of array references. If I use the range operator to print elements 1 through 3 of #array, print #array[1..3], perl prints the array references for elements 1 through 3.
Why when I try to dereference the array references indexed between 1 and 3, #{#array[1..3]}, perl only dereferences and prints out the last element indexed in the range operator?
Is there a way to use the range operator while dereferencing an array?
Example Code
#!/bin/perl
use strict;
use warnings;
my #array = ();
foreach my $i (0..10) {
push #array, [rand(1000), int(rand(3))];
}
foreach my $i (#array) {
print "#$i\n";
}
print "\n\n================\n\n";
print #{#array[1..3]};
print "\n\n================\n\n";
From perldata:
Slices in scalar context return the last item of the slice.
#{ ... } dereferences a scalar value as an array, this implies that the value being dereferenced is in scalar context. From the perldata quote above we know that this will return the last element. Therefore the result is the last element.
A more reasonable approach would be to loop through your slice and print each individual array reference:
use strict;
use warnings;
use feature qw(say);
my #array_of_arrayrefs = (
[qw(1 2 3)],
[qw(4 5 6)],
[qw(7 8 9)],
[qw(a b c)],
);
foreach my $aref ( #array_of_arrayrefs[1..3] ) {
say join ',', #$aref;
}
#{#array[1..3]} is a strange-looking construct. #{ ... } is the array dereference operator. It needs a reference, which is a type of scalar. But #array[ ... ] produces a list.
This is one of those situations where you need to remember the rule for list evaluation in scalar context. The rule is that there is no general rule. Each list-producing operator does its own thing. In this case, apparently the array slice operator used in scalar context returns the last element of the list. #array[1..3] in scalar context is the same as $array[3].
As you have noticed, this is not useful. Array slices aren't meant to be used in scalar context
If you want to flatten a 2-dimensional nested array structure into a 1-dimensional list, use map:
print join ' ', map { #$_ } #array[1..3]
You still use the range operator for slicing. You just need some kind of looping construct (e.g. map) to apply the array dereference operator separately to each element of the outer array.
The #{ ... } construction dereferences the scalar value of the code within the braces as an array
I'm unclear what you expect from #{ #array[1..3] }, but the list#array[1..3] in scalar context returns just the last element of the list -- $array[3] -- so you are asking for #{ $array[3] } which I guess is what you got
If you explain what you want to print then I am sure we can help, but dereferencing a list makes little sense
#array[1..3] is a list of 3 array references. You can't dereference them all at once, so you should iterate over this list and dereference each element separately:
print #$_ for #array[1..3];
print "#$_\n" for #array[1..3]; # for better looking output

Why I can use #list to call an array, but can't use %dict to call a hash in perl? [duplicate]

This question already has answers here:
Why do you need $ when accessing array and hash elements in Perl?
(9 answers)
Closed 8 years ago.
Today I start my perl journey, and now I'm exploring the data type.
My code looks like:
#list=(1,2,3,4,5);
%dict=(1,2,3,4,5);
print "$list[0]\n"; # using [ ] to wrap index
print "$dict{1}\n"; # using { } to wrap key
print "#list[2]\n";
print "%dict{2}\n";
it seems $ + var_name works for both array and hash, but # + var_name can be used to call an array, meanwhile % + var_name can't be used to call a hash.
Why?
#list[2] works because it is a slice of a list.
In Perl 5, a sigil indicates--in a non-technical sense--the context of your expression. Except from some of the non-standard behavior that slices have in a scalar context, the basic thought is that the sigil represents what you want to get out of the expression.
If you want a scalar out of a hash, it's $hash{key}.
If you want a scalar out of an array, it's $array[0]. However, Perl allows you to get slices of the aggregates. And that allows you to retrieve more than one value in a compact expression. Slices take a list of indexes. So,
#list = #hash{ qw<key1 key2> };
gives you a list of items from the hash. And,
#list2 = #list[0..3];
gives you the first four items from the array. --> For your case, #list[2] still has a "list" of indexes, it's just that list is the special case of a "list of one".
As scalar and list contexts were rather well defined, and there was no "hash context", it stayed pretty stable at $ for scalar and # for "lists" and until recently, Perl did not support addressing any variable with %. So neither %hash{#keys} nor %hash{key} had meaning. Now, however, you can dump out pairs of indexes with values by putting the % sigil on the front.
my %hash = qw<a 1 b 2>;
my #list = %hash{ qw<a b> }; # yields ( 'a', 1, 'b', 2 )
my #l2 = %list[0..2]; # yields ( 0, 'a', 1, '1', 2, 'b' )
So, I guess, if you have an older version of Perl, you can't, but if you have 5.20, you can.
But for a completist's sake, slices have a non-intuitive way that they work in a scalar context. Because the standard behavior of putting a list into a scalar context is to count the list, if a slice worked with that behavior:
( $item = #hash{ #keys } ) == scalar #keys;
Which would make the expression:
$item = #hash{ #keys };
no more valuable than:
scalar #keys;
So, Perl seems to treat it like the expression:
$s = ( $hash{$keys[0]}, $hash{$keys[1]}, ... , $hash{$keys[$#keys]} );
And when a comma-delimited list is evaluated in a scalar context, it assigns the last expression. So it really ends up that
$item = #hash{ #keys };
is no more valuable than:
$item = $hash{ $keys[-1] };
But it makes writing something like this:
$item = $hash{ source1(), source2(), #array3, $banana, ( map { "$_" } source4()};
slightly easier than writing:
$item = $hash{ [source1(), source2(), #array3, $banana, ( map { "$_" } source4()]->[-1] }
But only slightly.
Arrays are interpolated within double quotes, so you see the actual contents of the array printed.
On the other hand, %dict{1} works, but is not interpolated within double quotes. So, something like my %partial_dict = %dict{1,3} is valid and does what you expect i.e. %partial_dict will now have the value (1,2,3,4). But "%dict{1,3}" (in quotes) will still be printed as %dict{1,3}.
Perl Cookbook has some tips on printing hashes.

Dereferencing in case of $_[0], $_[1] ..... so on

please see the below code:
$scalar = 10;
subroutine(\$scalar);
sub subroutine {
my $subroutine_scalar = ${$_[0]}; #note you need the {} brackets, or this doesn't work!
print "$subroutine_scalar\n";
}
In the code above you can see the comment written "note you need the {} brackets, or this doesn't work!" . Please explain the reason that why we cant use the same statement as:
my $subroutine_scalar = $$_[0];
i.e. without using the curly brackets.
Many people have already given correct answers here. I wanted to add an example I found illuminating. You can read the documentation in perldoc perlref for more information.
Your problem is one of ambiguity, you have two operations $$ and [0] working on the same identifier _, and the result depends on which operation is performed first. We can make it less ambiguous by using the support curly braces ${ ... }. $$_[0] could (for a human anyway) possibly mean:
${$$_}[0] -- dereference the scalar $_, then take its first element.
${$_[0]} -- take element 0 of the array #_ and dereference it.
As you can see, these two cases refer to completely different variables, #_ and $_.
Of course, for Perl it is not ambiguous, we simply get the first option, since dereferencing is performed before key lookup. We need the support curly braces to override this dereferencing, and that is why your example does not "work" without support braces.
You might consider a slightly less confusing functionality for your subroutine. Instead of trying to do two things at once (get the argument and dereference it), you can do it in two stages:
sub foo {
my $n = shift;
print $$n;
}
Here, we take the first argument off #_ with shift, and then dereference it. Clean and simple.
Most often, you will not be using references to scalar variables, however. And in those cases, you can make use of the arrow operator ->
my #array = (1,2,3);
foo(\#array);
sub foo {
my $aref = shift;
print $aref->[0];
}
I find using the arrow operator to be preferable to the $$ syntax.
${ $x }[0] grabs the value of element 0 in the array referenced by $x.
${ $x[0] } grabs the value of scalar referenced by the element 0 of the array #x.
>perl -E"$x=['def']; #x=\'abc'; say ${ $x }[0];"
def
>perl -E"$x=['def']; #x=\'abc'; say ${ $x[0] };"
abc
$$x[0] is short for ${ $x }[0].
>perl -E"$x=['def']; #x=\'abc'; say $$x[0];"
def
my $subroutine_scalar = $$_[0];
is same as
my $subroutine_scalar = $_->[0]; # $_ is array reference
On the other hand,
my $subroutine_scalar = ${$_[0]};
dereferences scalar ref for first element of #_ array, and can be written as
my ($sref) = #_;
my $subroutine_scalar = ${$sref}; # or $$sref for short
Because $$_[0] means ${$_}[0].
Consider these two pieces of code which both print 10:
sub subroutine1 {
my $scalar = 10;
my $ref_scalar = \$scalar;
my #array = ($ref_scalar);
my $subroutine_scalar = ${$array[0]};
print "$subroutine_scalar\n";
}
sub subroutine2 {
my #array = (10);
my $ref_array = \#array;
my $subroutine_scalar = $$ref_array[0];
print "$subroutine_scalar\n";
}
In subroutine1, #array is an array containing the reference of $scalar. So the first step is to get the first element by $array[0], and then deference it.
While in subroutine2, #array is an array containing an scalar 10, and $ref_array is its reference. So the first step is to get the array by $ref_array, and then index the array.

How to get sub array?

I have the code below:
#a = ((1,2,3),("test","hello"));
print #a[1]
I was expecting it to print
testhello
But it gives me 2.
Sorry for the newbie question (Perl is a bit unnatural to me) but why does it happen and how can I get the result I want?
The way Perl constructs #a is such that it is equivalent to your writing,
#a = (1,2,3,"test","hello");
And that is why when you ask for the value at index 1 by writing #a[1] (really should be $a[1]), you get 2. To demonstrate this, if you were to do the following,
use strict;
use warnings;
my #a = ((1,2,3), ("test","hello"));
my #b = (1,2,3,"test","hello");
print "#a\n";
print "#b\n";
Both print the same line,
1 2 3 test hello
1 2 3 test hello
What you want is to create anonymous arrays within your array - something like this,
my #c = ([1,2,3], ["test","hello"]);
Then if you write the following,
use Data::Dumper;
print Dumper $c[1];
You will see this printed,
$VAR1 = [
'test',
'hello'
];
Perl lists are one-dimensional only, which means (1,2,(3,4)) is automatically flattened to (1,2,3,4). If you want a multidimensional array, you must use references for any dimension beyond the first (which are stored in scalars).
You can get any anonymous array reference with bracket notation [1,2,3,4] or reference an existing array with a backslash my $ref = \#somearray.
So a construct such as my $aref = [1,2,[3,4]] is an array reference in which the first element of the referenced array is 1, the second element is 2, and the third element is another array reference.
(I find when working with multidimensional arrays, that it's less confusing just to use references even for the first dimension, but my #array = (1,2,[3,4]) is fine too.)
By the way, when you stringify a perl reference, you get some gibberish indicating the type of reference and the memory location, like "ARRAY(0x7f977b02ac58)".
Dereference an array reference to an array with #, or get a specific element of the reference with ->.
Example:
my $ref = ['A','B',['C','D']];
print $ref; # prints ARRAY(0x001)
print join ',', #{$ref}; # prints A,B,ARRAY(0x002)
print join ',', #$ref; # prints A,B,ARRAY(0x002) (shortcut for above)
print $ref->[0]; # prints A
print $ref->[1]; # prints B
print $ref->[2]; # prints ARRAY(0x002)
print $ref->[2]->[0]; # prints C
print $ref->[2][0]; # prints C (shortcut for above)
print $ref->[2][1] # prints D
print join ',', #{$ref->[2]}; # prints C,D
I think you're after an array of arrays. So, you need to create an array of array references by using square brackets, like this:
#a = ([1,2,3],["test","hello"]);
Then you can print the second array as follows:
print #{$a[1]};
Which will give you the output you were expecting: testhello
It's just a matter of wrong syntax:
print $a[1]