How to get sub array? - perl

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]

Related

Why doesn't this deref of a reference work as a one-liner?

Given:
my #list1 = ('a');
my #list2 = ('b');
my #list0 = ( \#list1, \#list2 );
then
my #listRef = $list0[1];
my #list = #$listRef; # works
but
my #list = #$($list0[1]); # gives an error message
I can't figure out why. What am I missing?
There is one simple de-referencing rule that covers this. Loosely put:
What follows the sigil need be the correct reference for it, or a block that evaluates to that.
A specific case from perlreftut
You can always use an array reference, in curly braces, in place of the name of an array.
In your case then, it should be
my #list = #{ $list0[1] };
(not index [2] since your #list0 has two elements)  Spaces are there only for readability.
The attempted #$($list0[2]) is a syntax error, first because the ( (following the $) isn't allowed in an identifier (variable name), what presumably follows that $.
A block {} though would be allowed after the $ and would be evaluated, and must yield a scalar reference in this case, to be dereferenced by that $ in front of it; but then the first # would be in error. That can then fixed as well but this gets messy if pushed, and wasn't meant to go that far. While the exact rules are (still) a little murky, see Identifier Parsing in perldata.
The #$listRef earlier is correct syntax in general. But it refers to a scalar variable $listRef (which must be an array reference since it's getting dereferenced into an array by the first #), and there is no such a thing in the example -- you have an array variable #listRef.
So with use strict; in effect this, too, would fail to compile.
Dereferencing an arrayref to assign a new array is expensive as it has to copy all elements (and to construct the new array variable), while it's rarely needed (unless you actually want a copy). With the array reference on hand ($ar) all that one may need is readily available
#$ar; # list of elements
$ar->[$index]; # specific element
#$ar[#indices]; # slice -- list of some elements, like #$ar[0,2..5,-1]
$ar->#[0,-1]; # slice, with new "postfix dereferencing" (stable at v5.24)
$#$ar; # last index in the anonymous array referred by $ar
See Slices in perldata and Postfix reference slicing in perlref
You need
#{ $list0[1] }
Whenever you can use the name of a variable, you can use a block that evaluates to a reference. That means the syntax for getting the elements of an array are
#NAME # If you have the name
#BLOCK # If you have a reference
That means that
my #array1 = 4..5;
my #array2 = #array1;
and
my $array1 = [ 4..5 ];
my #array2 = #{ $array1 }
are equivalent.
When the only thing in the block is a simple scalar ($NAME or $BLOCK), you can omit the curlies. That means that
#{ $array1 }
is equivalent to
#$array1
That's why #$listRef works, and it's why #{ $list0[1] } can't be simplified.
See Perl Dereferencing Syntax.
You have a lot going on there and multiple levels of inadvertent references, so let's go through it:
First, you start by making a list of two items, each of which is an array reference. You store that in an array:
my #list0 = ( \#list2, \#list2 );
Then you ask for the item with index 2, which is a single item, and store that in an array:
my #listRef = $list0[2];
However, there is no item with index 2 because Perl indexes from zero. The value in #listRef in undefined. Not only that, but you've asked for a single item and stored it in an array instead of a scalar. That's probably not what you meant.
You say this following line works, but I don't think you know that because it won't give you the value you were expecting even if you didn't get an error. Something else is happening. You haven't declared or used a variable $listRef, so Perl creates it for you and gives it the value undef. When you try to dereference it, Perl uses "autovivification" to create the reference. This is the process where Perl helpfully creates a reference structure for you if you start with undef:
my #list = #$listRef; # works
There is nothing in that array so #list should be empty.
Fix that to get the last item, which has index of 1, and fix it so you are assigning the single value (the reference) to a scalar variable:
my $listRef = $list0[1];
Data::Dumper is handy here:
use Data::Dumper;
my #list2 = qw(a b c);
my #list0 = ( \#list2, \#list2 );
my $listRef = $list0[1];
print Dumper($listRef);
You get the output:
$VAR1 = [
'a',
'b',
'c'
];
Perl has some features that can catch these sorts of variable naming mistakes and will help you track down problems. Add these to the top of your program:
use strict;
use warnings;
For the rest, you might want to check out my book Intermediate Perl which explains all this reference stuff.
And, recent Perls have a new feature called postfix dereferencing that allows you to write dereferences from left to right:
my #items = ( \#list2, \#list2 );
my #items_of_last_ref = $items[1]->#*;
my #list = #$#listRef; # works
I doubt that works. That may not throw a syntax error but it sure as hell does not do what you think it does. For once
my #list0 = ( \#list2, \#list2 );
defines an array with 2 elements and you access
my #listRef = $list0[2];
the third element. So #listRef is an array that contains one element which is undef. The following code doesn't make sense either.
Unless the question is purely academic (answered by zdim already), I assume you want the second element of #list into a separate array, I would write
my #list = #{ $list0[1] };
The question is not complete and not clear on desired outcome.
OP tries to access an element $list0[2] of array #list0 which does not exist -- array has elements with indexes 0 and 1.
Perhaps #listRef should be $listRef instead in the post.
Bellow is my vision of described problem
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my #list1 = qw/word1 word2 word3 word4/;
my #list2 = 1000..1004;
my #list0 = (\#list1, \#list2);
my $ref_array = $list0[0];
map{ say } #{$ref_array};
$ref_array = $list0[1];
map{ say } #{$ref_array};
say "Element: " . #{$ref_array}[2];
output
word1
word2
word3
word4
1000
1001
1002
1003
1004
Element: 1002

How do references in Perl work

Can anyone explain the push statement in the following Perl code to me? I know how push in perl works but I can't understand what the first argument in following push command represents. I am trying to interpret someone's script. I tried to print "#a\n"; but it only printed ARRAY(0x9aa370) which makes me think that the push is not doing anything. Any help is appreciated. Thanks!
my #a = ();
my $b = 10;
my $c = 'a';
push(#{$a[$b]}, $c);
Let's break it down.
The #{...} is understood from "Using References" in perlref
Anywhere you'd put an identifier (or chain of identifiers) as part of a variable or subroutine name, you can replace the identifier with a BLOCK returning a reference of the correct type.
So what is inside { ... } block had better work out to an array reference. You have $a[$b] there, an element of #a at index $b, so that element must be an arrayref.
Then #{...} dereferences it and pushes a new element $c to it. Altogether, $c is copied into a (sole) element of an anonymous array whose reference is at index $b of the array #a.
And a crucial part: as there is in fact no arrayref † there, the autovivification kicks in and it is created. Since there are no elements at indices preceding $b they are created as well, with value undef.
Now please work through
tutorial perlreftut and
data-structures cookbook perldsc
while using perlref linked in the beginning for a full reference.
With complex data structures it is useful to be able to see them, and there are tools for that. A most often used one is the core Data::Dumper, and here is an example with Data::Dump
perl -MData::Dump=dd -wE'#ary = (1); push #{$ary[3]}, "ah"; dd \#ary'
with output
[1, undef, undef, ["ah"]]
where [] inside indicate an arrayref, with its sole element being the string ah.
† More precisely, an undef scalar is dereferenced and since this happens in an lvalue context the autovivification goes. Thanks to ikegami for a comment. See for instance this post with its links.
Let's start with the following two assertions:
#a starts out as an empty array with no elements.
$b is assigned the value of 10.
Now look at this construct:
#{$a[$b]}
To understand we can start in the middle: $a[$b] indexes element 10 of the array #a.
Now we can work outward from there: #{...} treats its contents as a reference to an array. So #{$a[$b]} treats the content of element 10 of the array #a as a reference to an anonymous array. That is to say, the scalar value contained in $a[10] is an array reference.
Now layer in the push:
push #{$a[$b]}, $c;
Into the anonymous array referenced in element 10 of #a you are pushing the value of $c, which is the character "a". You could access that element like this:
my $value = $a[10]->[0]; # character 'a'
Or shorthand,
my $value = $a[10][0];
If you pushed another value into #{$a[10]} then you would access it at:
my $other_value = $a[10][1];
But what about $a[0] through $a[9]? You're only pushing a value into $a[$b], which is $a[10]. Perl automatically extends the array to accommodate that 11th element ($a[10]), but leaves the value in $a[0] through $a[9] as undef. You mentioned that you tried this:
print "#a\n";
Interpolating an array into a string causes its elements to be printed with a space between each one. So you didn't see this:
ARRAY(0xa6f328)
You saw this:
ARRAY(0xa6f328)
...because there were ten spaces before the 11th element which contains an array reference.
If you were running your script with use warnings at the top, you would have seen this instead:
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
Use of uninitialized value in join or string at scripts/mytest.pl line 12.
ARRAY(0xa6f328)
...or something quite similar.
Your structure currently looks like this:
#a = (undef,undef,undef,undef,undef,undef,undef,undef,undef,undef,['a'])
If you ever want to really get a look at what a data structure looks like, rather than using a simple print, do something like this:
use Data::Dumper;
print Dumper \#a;
I've had a discussion over this yesterday here
what it means is that
#a is an array
$a[$b]
is a cell in the array
the #{} syntax helps perl understand that the cell in question is an array so you can preform push/pop operations on it.
if you do
use Data::Dumper;
print Dumper \#a;
you should see something like:
$VAR1 = [
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
[
'a'
]
];
as you can see, the 11th cell is an array containing the letter 'a' as its only value
the push operation on an empty cell could have also been written as:
$a[$b] = [$c]

Why can't I assign a variable to a hash entry in Perl?

Sorry, I'm super rusty with Perl. See the following code:
foreach my $hash (keys %greylist)
{
$t = $greylist{$hash};
print $greylist{$hash}[4] . "\n";
print $t[4] . "\n";
}
Why does $t[4] evaluate to a blank string, yet $greylist{$hash}[4] which should be the same thing evaluates to an IP address?
$greylist{$hash} contains an array reference. When you do:
print $greylist{$hash}[4];
Perl automatically treats it as an array reference but when you do:
$t = $greylist{$hash};
print $t[4];
You're assigning the array reference to a scalar variable, $t, then attempting to access the 5th element of another variable, #t. use strict would give you an error in this scenario.
Use the arrow operator, ->, to dereference:
$t = $greylist{$hash};
print $t->[4];
perlreftut has a note about this:
If $aref holds a reference to an array, then $aref->[3] is the fourth element of the array. Don't confuse this with $aref[3] , which is the fourth element of a totally different array, one deceptively named #aref . $aref and #aref are unrelated the same way that $item and #item are.

perl push many array in one array

I have many short array
#seq1 /773..1447/ #seq2 /1 2 1843..1881 1923..2001/
but i use push
push(#add, #seq1);
push(#add, #seq2);
but it shows like it combine all array into one can't get each sub-array any more
/773..1447 1 2 1843..1881 1923..2001/
when i use
$number=#add;
it shows 6, but it should be 2. Can anyone explain the reason and how to change it.
When i use for loop to add each array
for(..){
#temp= split(/,/,$_);
push(#add, \#temp);
}
Then when i print #add; it only shows memory address, How can show all data in #add
This is normal behavior, use reference to #seq1 if you want #add to be two dimensional array,
push(#add, \#seq1);
To print all values in #add you should use Data::Dumper; print Dumper \#add;
The reason is that all parameters get flattened into list when they are pushed into array, so
#a = #b = (1,2);
push(#add, #a, #b);
is same as writing
push(#add, $a[0],$a[1], $b[0],$b[1]);
Check perlref and perllol for reference.
the push command takes an ARRAY and a LIST. It is important to understand what happens here.
The first argument must be an ARRAY, the one you want to push things on. After that it expects a LIST. What this means is that this push statement provides list context to any #seq_n array - and sort of expands the array into separate elements. So all the elements of the #seq_n are being pushed onto your #add.
Since you did not want that to happen, you wanted an array that holds the separate lists - what we call in Perl a List-of-Lists - you actually wanted to push a reference to your #seq_n arrays, using the \ character.
push #add, \#seq_1, \#seq_2, . . . \#seq_n;
Now you have an array that indeed holds references to each $seq_n.
To print them neatly, each sequence on its own line, you could iterate over each
foreach my $seq (#add) {
# $seq holds a reference to a list!
my $string = join " ", #$seq; # the # dereferences the $seq
print $string, "\n";
}
but TIMTOWTDI
print map {(join " ", #$_), "\n"} #add;
Always consider the context in Perl, and try to embrace the charms of join, grep and map.

Perl "Not an ARRAY reference" error

I'll be glad if someone can enlighten me as to my mistake:
my %mymap;
#mymap{"balloon"} = {1,2,3};
print $mymap{"balloon"}[0] . "\n";
$mymap{'balloon'} is a hash not an array. The expression {1,2,3} creates a hash:
{
'1' => 2,
'3' => undef
}
You assigned it to a slice of %mymap corresponding to the list of keys: ('balloon'). Since the key list was 1 item and the value list was one item, you did the same thing as
$mymap{'balloon'} = { 1 => 2, 3 => undef };
If you had used strict and warnings it would have clued you in to your error. I got:
Scalar value #mymap{"balloon"} better written as $mymap{"balloon"} at - line 3.
Odd number of elements in anonymous hash at - line 3.
If you had used 'use strict; use warnings;' on the top of your code you probably have had better error messages.
What you're doing is creating a hash called mymap. A hash stores data as key => value pairs.
You're then assigning an array reference to the key balloon. Your small code snipped had two issues: 1. you did not addressed the mymap hash, 2. if you want to pass a list, you should use square brackets:
my %mymap;
$mymap{"balloon"} = [1,2,3];
print $mymap{"balloon"}[0] . "\n";
this prints '1'.
You can also just use an array:
my #balloon = (1,2,3);
print $balloon[0] . "\n";
Well, first off, always use strict; use warnings;. If you had, it might have told you about what is wrong here.
Here's what you do in your program:
my %mymap; # declare hash %mymap
#mymap{"balloon"} = {1,2,3}; # attempt to use a hash key on an undeclared
# array slice and assign an anonymous hash to it
print $mymap{"balloon"}[0] . "\n"; # print the first element of a scalar hash value
For it to do what you expect, do:
my %mymap = ( 'balloon' => [ 1,2,3 ] );
print $mymap{'balloon'}[0];
Okay, a few things...
%mymap is a hash. $mymap{"balloon"} is a scalar--namely, the value of the hash %mymap corresponding to the key "balloon". #mymap{"balloon"} is an attempt at what's called a hash slice--basically, you can use these to assign a bunch of values to a bunch of keys at once: #hash{#keys}=#values.
So, if you want to assign an array reference to $mymap{"balloon"}, you'd need something like:
$mymap{"balloon"}=[1,2,3].
To access the elements, you can use -> like so:
$mymap{"balloon"}->[0] #equals 1
$mymap{"balloon"}->[1] #equals 2
$mymap{"balloon"}->[2] #equals 3
Or, you can omit the arrows: $mymap{"balloon"}[0], etc.