Accessing arrays within arrays - perl

I have two bits of code that produce different output, and I am having great difficulty understanding why.
Code snippet 1:
my #args = $bighash{'arguments'}{'allocations'};
print "$args[0][1]";
Code snippet 2:
my #args = $bighash{'arguments'}{'allocations'}[0];
print "$args[1]";
In the first case, it is printing the value I expect. In the second case, it's not printing anything at all. Can anyone explain this?

The values stored in your hash are array references. You can work with the reference directly:
my $args = $bighash{'arguments'}{'allocations'};
print $$args[1]; # or $args->[1]
Or you can unpack the array into a new one:
my #args = #{ $bighash{'arguments'}{'allocations'} };
print $args[1];
More detail on the perlref and perldsc man pages.

Eric Strom's answer basically set's you right... but I'm surprised nobody has suggested using Data::Dumper to examine the data-structures you are working with. You can really see the difference between what you've done and Eric's correction. This might help set things straight for you.
Re: use warnings... you must get a warning when you try to print the non-existent element of the newly created array? I once got told never to ask anything online until I've used strict and warnings. That's maybe a bit extreme, but -w and Data::Dumper definitely help me :-)

In the code snippet 2, youre effectively taking the first element in your bighash ([0] is the first element) and putting that as the only element in an array. Then you ask for the second element, which does not exist.
If you remember to 'use warnings', this should yield one, since you're assigning a scalar as an array. I have not tested this myself, though.

Related

What must I do to prevent Perl from complaining that "using a hash as a reference is deprecated"?

The code below is from an old Perl script.
print "%{#{$noss}[$i]}->{$sector} \n\n";
How should I rewrite the code above so that Perl does not complain that "using a hash as a reference is deprecated"? I have tried all sorts of way but I still couldn't quite get the hang of what the Perl compiler want me to do.
print "%{#{$noss}[$i]}->{$sector} \n\n";
should be nothing more than
print "$noss->[$i]{$sector} \n\n";
or even
print "$$noss[$i]{$sector} \n\n";
without all that rigamarole.
Guessing that $noss is a reference to an array of hash references, you can build a correct expression by following the simple rule of replacing
what would normally be an array or hash name (not including the $/#/%) with an expression giving a reference in curly braces.
So your array element, normally $foo[$i], becomes ${$noss}[$i]. That expression is itself a hashref, so to get an element from that hash, instead of $foo{$sector}, you use ${ ${$noss}[$i] }{$sector}.
This can also appear in various other forms, such as $noss->[$i]{$sector}; see http://perlmonks.org?node=References+quick+reference for simple to understand rules.
I agree with ysth and tchrist, and want to reiterate that $noss->[$i]{$sector} really is the best option for you. This syntax is more readable since it shows clearly that $noss is a reference and that you are taking the $ith element of it and further the $sector key from that element.
In terms of teaching to fish rather than giving out fish: you should read perldoc perlreftut and specifically the "use rules". Understanding these two "use rules" along with the extra "arrow rule" (yep only 3 rules) will give you a much better grasp on how to get going with references.

Perl : In splice() type of arg1 must be array, cannot be scalar dereference. How to fix?

I am trying to comprehend the reference/dereference system in Perl.
What I am trying to do is to remove an element by using reference:
my $ref= \#{$collection{$_[0]}};
# delete($$ref[$i]); # delete works, I've tested that already
splice($$ref, $i, 1); # this wouldn't do.
I first tried the delete() subroutine, it works; however, it doesn't shift the index after the removed elements forward by 1, so I cannot continue working on other stuff.
I then Googled and found the splice() subroutine which does delete and shift in one go.
But the error feedback tells me that
"Type of arg 1 to splice must be array (not scalar dereference)..."
I then tried something like this:
splice(#{$$ref}, $i, 1);
That resulted in another error like this:
"Not a SCALAR reference at...(pointing at that line)"
So I am a bit puzzled, how could I handle this issue?
I prefer not using any CPAN or additional library for the solution, if possible.
splice(#$ref, $i, 1); # this works...
Ahhh... I encountered this question last night (2am) so my energy was burnt out...
Now I see the magic in Perl a bit more clearly :)
Sorry about such a simple question.

What is Perl doing with this argument to push in this case?

I just saw some code in our code base (and it's OLD code, as in Perl 3 or Perl 4 days) that looks like this (I'm simplifying greatly):
#array;
push( array, $some_scalar );
Notice that the array in the push() doesn't have an #. I would assume that the code behind push knows that the first argument is supposed to be array so grabs the array from the array typeglob. Is that more or less it? If Perl is able to do that without problem, why would you need to include the # at all?
This is an old 'feature' of the parser. The # isn't mandatory in a push if the variable is a package variable. This is considered by many as a bug that ought to be fixed though. You really shouldn't be doing this.
This is a dubious "feature" of perl, deprecated behaviour; it should be an error, but it works.
If you turn on the warnings of the compiler (perl -W, highly recommended), it warns:
Array #aa missing the # in argument 1 of push() at xx.pl line 2.
Nicholas Clark explains:That's Perl 1 syntax.

How do I create new variables with indexed names in Perl?

hi i've read some related question non seems tohelp me.
this is a code that will explain what i want.
for ($i=0;$i<5;$i++) {
my $new$i=$i;
print "\$new$i is $new$i";
}
expecting variables to be named $new0,$new1,$new2,$new3,$new4,$new5.
and to have the abillty to use them in a loop like the print command is trying to do.
Thanks
You want to use a hash or array instead. It allows a collection of data to remain together and will result in less pain down the line.
my %hash;
for my $i (0..4) {
$hash{$i} = $i;
print "\$hash{$i} is $hash{$i}\n";
}
Can you describe why exactly you need to do this.
Mark Jason Dominus has written why doing this is not such a good idea in Why it's stupid to "use a variable as a variable name"
part 1, part 2 and part 3.
If you think your need is an exception to cases described there, do let us know how and somebody here might help.
You are asking a common question. The answer in 99.9% of cases is DON'T DO THAT.
The question is so common that it is in perlfaq7: How can I use a variable as a variable name?.
See "How do I use symbolic references in Perl?" for lots of discussion of the issues with symbolic references.
use a hash instead.
my %h=();
$new="test";
for ($i=0;$i<5;$i++) {
$h{"$new$i"}=$i;
}
while( my( $key, $value ) = each( %h ) ) {
print "$key,$value\n";
}
if you want to create variables like $newN you can use eval:
eval("\$new$i=$i");
(using hash probably would be better)

Are #{$list_ref} and #$list_ref equivalent in Perl?

I am new to Perl and am curious whether #{$list_ref} and #$list_ref are perfectly equivalent.
They seem to be interchangeable, but I am nervous that there is something subtle going on that I may be missing.
Yes, they're equivalent. You need braces when the expression is more than a simple scalar variable, e.g.,
push #{ $foo{$bar} } => "baz";
For more detail, see the Using References section of the documentation on references. The standard Perl documentation also includes several tutorials on using references:
Understand References Today (mentioned by hobbs in the question's comments)
Manipulating Arrays of Arrays in Perl
Perl Data Structures Cookbook
I've always found it helpful to remember that the outer braces are not syntactical magic, they're just a block that returns a reference. The expression inside the block can be anything that returns a reference:
$ perl -le 'sub foo {[qw/ apple orange banana /]} print ${print "Do something here."; foo()} [1]'
Do something here.
orange