How do I access the array's element stored in my hash in Perl? - perl

# I have a hash
my %my_hash;
# I have an array
#my_array = ["aa" , "bbb"];
# I store the array in my hash
$my_hash{"Kunjan"} = #my_array;
# But I can't print my array's element
print $my_hash{"Kunjan"}[0];
I am new to Perl. Please help me.

Your array syntax is incorrect. You are creating an anonymous list reference, and #my_array is a single-element list containing that reference.
You can either work with the reference properly, as a scalar:
$my_array = ["aa" , "bbb"];
$my_hash{"Kunjan"} = $my_array;
Or you can work with the list as a list, creating the reference only when putting it into the hash:
#my_array = ("aa" , "bbb");
$my_hash{"Kunjan"} = \#my_array;

If you had only put this at the top of your script:
use strict;
use warnings;
...you would have gotten some error messages indicating what was wrong:
Global symbol "#my_array" requires explicit package name at kunjan-array.pl line 8.
Global symbol "#my_array" requires explicit package name at kunjan-array.pl line 11.
So, declare the array first with my #my_array; and then you would get:
Can't use string ("1") as an ARRAY ref while "strict refs" in use at kunjan-array.pl line 14.
You created an arrayref and attempted to assign it to an array - see perldoc perldata for how to declare an array
You attempted to assign an array to a hash (you can only assign scalars, such as an arrayref - see perldoc perlref for more about references)
You need to dereference the hash element to get at the array element, e.g. $my_hash{"Kunjan"}->[0] - again see perldoc perlref for how to dereference a hashref

You have a few errors in your program:
my #my_array = ("aa" , "bbb");
$my_hash{"Kunjan"} = \#my_array;
print $my_hash{"Kunjan"}[0];
I made three changes:
Added my in front of #my_array on the first line
Change the [...] to (...) on the first line
Add a \ in front of #my_array on the second line

Try these amendments:
my %my_hash;
# ["aa" , "bbb"] produces an array reference. Use () instead
my #my_array = ("aa" , "bbb");
# 'Kunjan' hash is given reference to #my_array
$my_hash{ Kunjan } = \#my_array;
# bareword for hash key is nicer on the eye IMHO
print $my_hash{ Kunjan }[0];
However there is still one thing you need to consider if you use this method:
unshift #my_array, 'AA';
print $my_hash{ Kunjan }[0]; # => AA - probably not what u wanted!
So what you are probably after is:
$my_hash{ Kunjan } = ["aa" , "bbb"];
Then the hash is no longer referencing #my_array.
/I3az/

Others already explained nicely what's what, but I would like to add, that (especially if you're new to Perl), it would be great if you spend some time and read the perldsc and perllol docs.

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]

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.

Confusion about proper usage of dereference in Perl

I noticed the other day that - while altering values in a hash - that when you dereference a hash in Perl, you actually are making a copy of that hash. To confirm I wrote this quick little script:
#! perl
use warnings;
use strict;
my %h = ();
my $hRef = \%h;
my %h2 = %{$hRef};
my $h2Ref = \%h2;
if($hRef eq $h2Ref) {
print "\n\tThey're the same $hRef $h2Ref";
}
else {
print "\n\tThey're NOT the same $hRef $h2Ref";
}
print "\n\n";
The output:
They're NOT the same HASH(0x10ff6848) HASH(0x10fede18)
This leads me to realize that there could be spots in some of my scripts where they aren't behaving as expected. Why is it even like this in the first place? If you're passing or returning a hash, it would be more natural to assume that dereferencing the hash would allow me to alter the values of the hash being dereferenced. Instead I'm just making copies all over the place without any real need/reason to beyond making syntax a little more obvious.
I realize the fact that I hadn't even noticed this until now shows its probably not that big of a deal (in terms of the need to go fix in all of my scripts - but important going forward). I think its going to be pretty rare to see noticeable performance differences out of this, but that doesn't alter the fact that I'm still confused.
Is this by design in perl? Is there some explicit reason I don't know about for this; or is this just known and you - as the programmer - expected to know and write scripts accordingly?
The problem is that you are making a copy of the hash to work with in this line:
my %h2 = %{$hRef};
And that is understandable, since many posts here on SO use that idiom to make a local name for a hash, without explaining that it is actually making a copy.
In Perl, a hash is a plural value, just like an array. This means that in list context (such as you get when assigning to a hash) the aggregate is taken apart into a list of its contents. This list of pairs is then assembled into a new hash as shown.
What you want to do is work with the reference directly.
for (keys %$hRef) {...}
for (values %$href) {...}
my $x = $href->{some_key};
# or
my $x = $$href{some_key};
$$href{new_key} = 'new_value';
When working with a normal hash, you have the sigil which is either a % when talking about the entire hash, a $ when talking about a single element, and # when talking about a slice. Each of these sigils is then followed by an identifier.
%hash # whole hash
$hash{key} # element
#hash{qw(a b)} # slice
To work with a reference named $href simply replace the string hash in the above code with $href. In other words, $href is the complete name of the identifier:
%$href # whole hash
$$href{key} # element
#$href{qw(a b)} # slice
Each of these could be written in a more verbose form as:
%{$href}
${$href}{key}
#{$href}{qw(a b)}
Which is again a substitution of the string '$href' for 'hash' as the name of the identifier.
%{hash}
${hash}{key}
#{hash}{qw(a b)}
You can also use a dereferencing arrow when working with an element:
$hash->{key} # exactly the same as $$hash{key}
But I prefer the doubled sigil syntax since it is similar to the whole aggregate and slice syntax, as well as the normal non-reference syntax.
So to sum up, any time you write something like this:
my #array = #$array_ref;
my %hash = %$hash_ref;
You will be making a copy of the first level of each aggregate. When using the dereferencing syntax directly, you will be working on the actual values, and not a copy.
If you want a REAL local name for a hash, but want to work on the same hash, you can use the local keyword to create an alias.
sub some_sub {
my $hash_ref = shift;
our %hash; # declare a lexical name for the global %{__PACKAGE__::hash}
local *hash = \%$hash_ref;
# install the hash ref into the glob
# the `\%` bit ensures we have a hash ref
# use %hash here, all changes will be made to $hash_ref
} # local unwinds here, restoring the global to its previous value if any
That is the pure Perl way of aliasing. If you want to use a my variable to hold the alias, you can use the module Data::Alias
You are confusing the actions of dereferencing, which does not inherently create a copy, and using a hash in list context and assigning that list, which does. $hashref->{'a'} is a dereference, but most certainly does affect the original hash. This is true for $#$arrayref or values(%$hashref) also.
Without the assignment, just the list context %$hashref is a mixed beast; the resulting list contains copies of the hash keys but aliases to the actual hash values. You can see this in action:
$ perl -wle'$x={"a".."f"}; for (%$x) { $_=chr(ord($_)+10) }; print %$x'
epcnal
vs.
$ perl -wle'$x={"a".."f"}; %y=%$x; for (%y) { $_=chr(ord($_)+10) }; print %$x; print %y'
efcdab
epcnal
but %$hashref isn't acting any differently than %hash here.
No, dereferencing does not create a copy of the referent. It's my that creates a new variable.
$ perl -E'
my %h1; my $h1 = \%h1;
my %h2; my $h2 = \%h2;
say $h1;
say $h2;
say $h1 == $h2 ?1:0;
'
HASH(0x83b62e0)
HASH(0x83b6340)
0
$ perl -E'
my %h;
my $h1 = \%h;
my $h2 = \%h;
say $h1;
say $h2;
say $h1 == $h2 ?1:0;
'
HASH(0x9eae2d8)
HASH(0x9eae2d8)
1
No, $#{$someArrayHashRef} does not create a new array.
If perl did what you suggest, then variables would get aliased very easily, which would be far more confusing. As it is, you can alias variables with globbing, but you need to do so explicitly.

How to clear a Perl hash

Let's say we define an anonymous hash like this:
my $hash = {};
And then use the hash afterwards. Then it's time to empty or clear the hash for
reuse. After some Google searching, I found:
%{$hash} = ()
and:
undef %{$hash}
Both will serve my needs. What's the difference between the two? Are they both identical ways to empty a hash?
%$hash_ref = (); makes more sense than undef-ing the hash. Undef-ing the hash says that you're done with the hash. Assigning an empty list says you just want an empty hash.
Yes, they are absolutely identical. Both remove any existing keys and values from the table and sets the hash to the empty list.
See perldoc -f undef:
undef EXPR
undef Undefines the value of EXPR, which must be an lvalue. Use only
on a scalar value, an array (using "#"), a hash (using "%"), a
subroutine (using "&"), or a typeglob (using "*")...
Examples:
undef $foo;
undef $bar{'blurfl'}; # Compare to: delete $bar{'blurfl'};
undef #ary;
undef %hash;
However, you should not use undef to remove the value of anything except a scalar. For other variable types, set it to the "empty" version of that type -- e.g. for arrays or hashes, #foo = (); %bar = ();