Why does my perl hash deform my array data? - perl

I have encountered some interesting behavior in Perl, and was hoping to be enlightened.
As you can see I create an array called #tuple that has two array references. When I assign it to a key and extract it I only get the first array VALUE as apposed to the reference. Can anyone tell me why this is happening?
my #VMstoreName = ($storeName[$i]);
my #VMdiskCap = ($diskCap[$i]);
print "VMstoreName : ". join(' ', #VMstoreName) . "\n";
print "VMdiskCap : ". join(' ', #VMdiskCap) . "\n";
# Initializing our key
my #tuple = (\#VMstoreName, \#VMdiskCap);
print "After tuple " . join(' ', #tuple) . "\n";
#virtualMachines{$vmNames[$i]} = #tuple;
my #DEBUG = #{$virtualMachines{$vmNames[$i]}};
print "After first map : " . join(' ', #DEBUG) . "\n";
print "WHAT??? $DEBUG[0] $DEBUG[1]!!!\n";
Here is the output, I want After first map to read exactly like After tuple
VMstoreName : 172.16.1.3:/export/Paul/test-0
VMdiskCap : 1
After tuple ARRAY(0x2c4ccf0) ARRAY(0x2c4cd38)
After first map 172.16.1.3:/export/Paul/test-0
WHAT??? 172.16.1.3:/export/Paul/test-0 !!!

Your problem begins here (I guess):
#virtualMachines{$vmNames[$i]} = #tuple;
#hash{...} is a hash slice: It accesses mutiple entries in the hash at once (this may be only one entry). A slice as LHS value imposes list context.
To this list we assign the values of #tuple.
Now list assignment has the property of forgetting superfluous values:
my ($x, $y) = (1, 2, 3); # nobody likes № 3
So the above statement is the same as
$virtualMachines{ $vmNames[$i] } = $tuple[0];
Specifically, this assigns an array reference to the %virtualMachines entry, as #tuple only holds arrayrefs.
On the next line, you access this entry via $virtualMachines{$vmNames[$i]}. This evaluates to an array reference. You then dereference this to an array with #{ ... }.
This assigns all the values of that array to #DEBUG, not the array reference. As seen in your debug statement, this is the VMstoreName.
Solution
Assign an arrayref to the %virtualMachines entry:
$virtualMachines{ $vmNames[$i] } = \#tuple;
Arrays and hashes can only hold scalars, so we can't use an array as value, only an array reference.

You are assigning to your Hash in list context using the same key. Keys have to be unique in hashes, so only one reference gets stored inside of your Hash. The Hash-slice is only 1-element wide so the second reference just gets ignored.
You should assign a reference to #tuple instead using scalar context:
$virtualMachines{$vmNames[$i]} = \#tuple;

You are passing references to arrays when you initialize #tuple. Instead, just pass the arrays:
my #tuple = (#VMstoreName, #VMdiskCap);

It looks like #tuple is an array of array references. You'll have to deference each element to print them out the way you intend to.
my #tuple = (\#VMstoreName, \#VMdiskCap);
print "After tuple " . join(' ', map { #$_ } #tuple) . "\n";

Related

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

Inconsistent (silly?) data access in Perl 5 (also confusing me regarding use of sigils)

This question is about asking for some explanation of what's going on in the Perl system for I don't implicitly see the point though I'm coding for more than 25 years now. So here comes the story ...
On trying to work with Cyrus::IMAP::Admin instances in Perl5 I've tried to get and print a list of quotas resulting in a somewhat strangely structured data returned.
my %quotas = $client->listquota(#list[0]);
if ( $client->error ) {
printf STDERR "Error: " . $client->error . "\n";
exit 1;
}
print "root: " . $list[0] . "\n";
foreach my $quota ( keys %quotas ) {
print( $quota, " ", $quotas{$quota}[0], "/", $quotas{$quota}[1], " KiB\n" );
}
This code is actually working as desired by printing out stuff like
root: user.myuser
STORAGE: 123/4567 KiB
This code was taken from Cyrus::IMAP::Shell reading similar to this:
my %quota = $$cyrref->listquota(#nargv);
foreach my $quota (keys %quota) {
$lfh->[1]->print(" ", $quota, " ", $quota{$quota}[0], "/", $quota{$quota}[1]);
if ($quota{$quota}[1]) {
$lfh->[1]->print(" (", $quota{$quota}[0] * 100 / $quota{$quota}[1], "%)");
}
}
This code looks somewhat silly to me for using $quota{$quota}[0]. In my case I renamed variables a bit for rejecting that mixed use of differently typed but equivalently named variables.
Prior to taking the code from Cyrus::IMAP::Admin I tried to understand its specification and to process the result by code written myself. It looked like this:
my %quotas = $client->listquota(#list[0]);
if ( $client->error ) {
printf STDERR "Error: " . $client->error . "\n";
exit 1;
}
print "root: " . $list[0] . "\n";
foreach my $quota ( keys %quotas ) {
my #sizes = #quotas{$quota};
print( $quota, " ", $sizes[0], "/", $sizes[1], "\n" );
}
However, this code didn't work and I didn't find any plausible explanation myself. My understanding here is that transferring this last code example to the initially posted form would require to have source of assignment in line 11 substituted into usages in line 12 and change the sigil of quotas from # to $ for I'm trying to get a scalar result finally. This last code was printing an array reference before slash and nothing after it. So I had to fix my code like this to get it working:
my %quotas = $client->listquota(#list[0]);
if ( $client->error ) {
printf STDERR "Error: " . $client->error . "\n";
exit 1;
}
print "root: " . $list[0] . "\n";
foreach my $quota ( keys %quotas ) {
my #sizes = #quotas{$quota};
print( $quota, " ", $sizes[0][0], "/", $sizes[0][1], "\n" );
}
This additional dereferencing in line 12 is what I'm confused about now. Why is #sizes containing an array storing another array in its sole first element? For being confused I already tried alternative code in line 11 to no avail. These tests included
my #sizes = $quotas{$quota};
(for its equivalence with original code posted above) and
my $sizes = #quotas{$quota};
(for I don't know why). Switching sigils don't seem to change semantics of assignment here at all. But using this assignment seems to open different view on data structure contained in %quotas originally. What sigils are required to have #sizes matching content and structure of $quotas{$quota} as used in top-most code fragment?
$quotas{$quota} accesses a single scalar element in %quotas. #quotas{$quota} is a hash slice which selects a list of one element from the hash.
$collection[...] or $collection{...}: Single scalar element
my #array = (1, 2, 3, 4);
my $elem = $array[1]; #=> 2
#collection[...] or #collection{...}: list of elements
my #array = (1, 2, 3, 4);
my #slice = #array[1, 3]; #=> (2, 4)
%collection[...] or %collection{...}: key-value list, not yet available except in “blead”.
my #array = (1, 2, 3, 4);
my %kvs = %array[1, 3]; #=> (1 => 2, 3 => 4)
Using another sigil when referencing an element in a collection does not dereference the element!
Now what does or single-element list #quotas{$quota} contain? It is an array reference [...].
When you assign this to a scalar, the list is evaluated in scalar context and gives the last element:
my $sizes = #quotas{$quota};
my $sizes = ([...]);
my $sizes = [...];
[...]
Accessing an element in that array reference then looks like $sizes->[0].
When you assign this to an array, you create an array that holds as a single element this array reference:
my #sizes = #quotas{$quota};
my #sizes = ([...]);
([...])
Accessing an element in that array reference then looks like $sizes[0][0], because you first have to get at that array reference inside the array #sizes.
… and incidentally the same happens here when you do $quotas{$quota}, but for slightly different reasons.
If you want to dereference an array reference to an array, use curly braces:
my #foo = #{$array_refernce} which copies the contents
my $elem = ${$array_reference}[0] which accesses an element, the same as $array_reference->[0].
So you could do
my #sizes = #{ $quotas{$quota} };
to dereference the array and copy it to an array variable. You can then access an element like $sizes[0].
I believe you want this on your line 11:
my #sizes = #{ $quotas{$quota} };
Also, recommend you start using Data::Dumper everywhere.
E.g.
use Data::Dumper;
print 'Data structure of \%quotas: ' . Dumper(\%quotas) . qq(\n);
That way you can be sure what structure you are dealing with.

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.