Perl looping through a hash produces strange value - perl

I'm using Perl to parse the output from objdump. I have the following code:
#!/usr/bin/perl
%count = {};
while (<>) {
if (/^\s+[[:xdigit:]]+:\s+[[:xdigit:]]+\s+([a-z]+).+$/) {
++$count{"$1"};
}
}
while (($key, $val) = each %count) {
print "$key $val\n";
}
In the resulting output, most parts are okay like this:
strhib 2
strcc 167
stmlsda 4
swivc 21
ldmlsia 4
But there is one strange line:
HASH(0x8ae2158)
What's going on here? I expect $1 to be a string, and ++$count{"$1"} should be perfectly fine.
Thank you.
So the correct code should be:
#!/usr/bin/perl
use strict;
my %count;
while (<>) {
if (/^\s+[[:xdigit:]]+:\s+[[:xdigit:]]+\s+([a-z]+).+$/) {
++$count{"$1"};
}
}
while (my ($key, $val) = each %count) {
print "$key $val\n";
}

If you had use warnings; you would have seen: "Reference found where even-sized list expected". Instead of
%count = {};
you should have said
my %count;
What you wrote was equivalent to this:
%count = ({} => undef);
That is, you initialized your hash with an empty hashref as a key with no associated value. The hashref stringified to "HASH(0x8ae2158)" (the number may change). To clear out a hash, you use parens () not braces {}. Braces construct a hash reference.
Even short programs like this should start with:
use strict;
use warnings;
The bugs you catch will be your own. :-)
The warnings pragma is preferred to the -w switch because it acts lexically. See What's wrong with -w and $^W in perllexwarn.

Related

How foreach loop in perl works with arrays in perl

this program should execute three times but is executing only twice.
Can anyone explain how this foreach loop will work in perl.
#!/usr/bin/perl
use strict;
use warnings;
my #arr=("sandeepr", "vijay","vikas");
for my $i( #arr)
{
print #arr;
my $b=pop(#arr);
print "\n $b";
}
perlsyn:
If any part of LIST is an array, foreach will get very confused if you add or remove elements within the loop body, for example with splice. So don't do that.
As confused as this makes Perl, you appear to be even more confused. What are trying to do? Print the elements in reverse order? If so, you could use
for my $ele (reverse #arr) {
print("$ele\n");
}
or
for my $i (1..#arr) {
my $ele = $arr[-$i];
print("$ele\n");
}
or
while (#arr) {
my $ele = pop(#arr);
print("$ele\n");
}

Perl: Printing out the file where a word occurs

I am trying to write a small program that takes from command line file(s) and prints out the number of occurrence of a word from all files and in which file it occurs. The first part, finding the number of occurrence of a word, seems to work well.
However, I am struggling with the second part, namely, finding in which file (i.e. file name) the word occurs. I am thinking of using an array that stores the word but don’t know if this is the best way, or what is the best way.
This is the code I have so far and seems to work well for the part that counts the number of times a word occurs in given file(s):
use strict;
use warnings;
my %count;
while (<>) {
my $casefoldstr = lc $_;
foreach my $str ($casefoldstr =~ /\w+/g) {
$count{$str}++;
}
}
foreach my $str (sort keys %count) {
printf "$str $count{$str}:\n";
}
The filename is accessible through $ARGV.
You can use this to build a nested hash with the filename and word as keys:
use strict;
use warnings;
use List::Util 'sum';
while (<>) {
$count{$word}{$ARGV}++ for map +lc, /\w+/g;
}
foreach my $word ( keys %count ) {
my #files = keys %$word; # All files containing lc $word
print "Total word count for '$word': ", sum( #{ $count{$word} }{#files} ), "\n";
for my $file ( #files ) {
print "$count{$word}{$file} counts of '$word' detected in '$file'\n";
}
}
Using an array seems reasonable, if you don't visit any file more than once - then you can always just check the last value stored in the array. Otherwise, use a hash.
#!/usr/bin/perl
use warnings;
use strict;
my %count;
my %in_file;
while (<>) {
my $casefoldstr = lc;
for my $str ($casefoldstr =~ /\w+/g) {
++$count{$str};
push #{ $in_file{$str} }, $ARGV
unless ref $in_file{$str} && $in_file{$str}[-1] eq $ARGV;
}
}
foreach my $str (sort keys %count) {
printf "$str $count{$str}: #{ $in_file{$str} }\n";
}

Perl: How do I get the value of a variable in a loop if it is in a1 a2 a3 format

Basically i am trying to access the predefined variable in a perl program.
the variables are in the form a1 a2 a3 format.
I want to access them in a loop. In the loop I will increment postfix scalar value
#!/usr/bin/perl
use strict;
use warnings;
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
my $var = ${a$i};
print $var;
}
WHAT I EXPECT:
When I print $var in loop, I need the values 10,12 .. defined earlier.
WHAT I CAN NOT DO:
I am aware that such situation can be handled with a hash. But I do not have any control over the variable naming, hence I can not use hash or change variable format.
I appreciate your help!
If you want to avoid turning off strict, you could use eval:
#!/usr/bin/perl
use strict;
use warnings;
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
print eval "\$a$i";
}
Update: using more readable version suggested by Сухой27 in the comments
Use an array instead of multiple similarly named variables, as this is their main use case,
use strict;
use warnings;
my #a = (10,12,14,16);
for my $i (0 .. $#a) {
my $var = $a[$i];
print $var, "\n";
}
alternatively you can use array of scalar references
use strict;
use warnings;
my ($a0,$a1,$a2,$a3) = (10,12,14,16);
my #a = \($a0,$a1,$a2,$a3);
for my $i (0 .. $#a) {
my $var = ${ $a[$i] };
print $var, "\n";
}
What you are doing here is called a symbolic reference, and it is an EXTREMELY bad idea.
Please take a look through this article: http://perl.plover.com/varvarname.html
But the long and short of it is - using a variable as a variable name - which you're doing - is dangerous and unnecessary. It causes all sorts of potential problems in your code, including bugs in completely unrelated pieces of code. This is why strict won't let you do it.
More importantly - it's completely unnecessary, because perl has the hash as a native data type.
Instead of your code, consider instead:
my %h;
( $h{0}, $h{1}, $h{2}, $h{3} ) = ( 10, 12, 14, 16 );
foreach my $key ( sort keys %h ) {
print "$key = $h{$key}\n";
}
Now, it's added a few characters to your code, but by doing so - you've created a lexically scoped namespace called %h. (I'd suggest calling it something more meaningful, personally - and definitely avoid $a and $b because they have special meanings).
But there is no danger of this namespace trampling over other parts of your code, and for bonus points - you no longer need your 'for' loop, you can simply iterate on keys instead. (So you always have the right number).
(Or as another user has suggested - just use an array)
You can get round strict's restriction on dynamic variable names like this.
#!/usr/bin/perl
use strict;
use warnings;
{
no strict 'refs';
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
my $var = ${a$i};
print $var;
}
}
I don't think this is a good idea, though!

Perl 2x2 array addition using Subroutines and Reference

My question in Perl is:
Define 2x2 arrays using anonymous lists. Pass the arrays to a subroutine and add them together. Return a reference to sum array and print the values from the main part of the program.
My script is:
#!/usr/bin/perl
use strict;
use warnings;
my #array = ([1,2],[4,5]);
my $refarray = \#array;
print sumarray($refarray);
sub sumarray
{
$refarray = shift;
foreach (#{$refarray})
{
$refarray = ($refarray[0]->[0]+$refarray[1]->[0],$refarray[0]->[1]+$refarray[1]->[1]);
}
return $refarray;
}
Where am I going wrong? Please help. Thanks in advance.
I am getting the output as 0.
If I use use strict; and use warnings; I will get the error message as
Global symbol "#refarray" requires explicit package name at line 23.
Global symbol "#refarray" requires explicit package name at line 23.
Global symbol "#refarray" requires explicit package name at line 23.
Global symbol "#refarray" requires explicit package name at line 23.
Execution aborted due to compilation errors.
Few problems with your code: -
First, in your for-loop, you are modifying your reference $refarray which you should not do.
Second, $refarray[0]->[0] will not compile. Since $refarray is a reference to an array, you should either use its 1st element using arrow: - $refarray->[0][0], or you need to de-reference it before using the way you are using: - $$refarray[0]->[0].
Having said that, I think you should replace your subroutine with this one: -
use strict;
use warnings;
my #array = ([1,2],[4,5]);
my $refarray = \#array;
my $sum = sumarray($refarray);
print $sum->[0], $sum->[1];
sub sumarray {
my $ref = shift;
return [$ref->[0][0] + $ref->[1][0], $ref->[0][1] + $ref->[1][1]];
}
OUTPUT: -
5 7
Try this:
#!/usr/bin/perl -w
my $r = sumarray([1,2],[3,4]);
print $r->[0], " ", $r->[1], "\n";
sub sumarray {
my ($a, $b) = #_;
return [
$a->[0]+$b->[0],
$a->[1]+$b->[1]
];
}
It could be expressed very simply with a combination of the list operations sum (from the core module List::Util) and map.
Code
#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use List::Util 'sum';
sub sum_square {
my #arrays = #_;
return [ map {sum #$_} #arrays ];
}
say join ', ' => #{ sum_square([1,2], [4,5]) };
Output:
3, 9
Since this is Perl, it could be expressed in a more compact way.
Shortification
sub sum_square { [ map {sum #$_} #_ ] }
say join ', ' => #{ sum_square([1,2], [4,5]) };
Shortification2
say join ', ' => map {sum #$_} [1,2], [4,5];
Edit: sum the other way round
If the function should be a column sum instead of a line sum, this modification should do the trick (iterate over the indices of the first array):
sub sum_square {
my #arrays = #_;
return [ map { my $i = $_; # $i: all indices of the first array
sum map $_->[$i] => #arrays # sum over all $i-th values of #arrays
} 0 .. $#{$arrays[0]} ];
}
Output:
5, 7

Why does escaping of # disappear when read from an array?

I have a string,
my $element="abc#$def"
I escape # using,
$element=~s/#/\\#/g;
It is printed as: abc\#$def, which is perfect.
Next part of the code is:
push(#arr,$element);
foreach $val (#arr)
{
print $val;
}
And the value printed within the foreach loop is: abc#$def.
Why is # not escaped here? And how can I retain the escaping?
You're not quite showing us everything. To get your claimed result, I had to create the variable $def initialized as shown below. But, when I do that, I get the result you expect, not the result you show.
$ cat xx.pl
use strict;
use warnings;
my $def = '$def';
my $element = "abc#$def";
$element =~ s/#/\\#/g;
print "$element\n";
my #arr;
push(#arr, $element);
foreach my $val (#arr)
{
print $val;
print "\n";
}
$ perl xx.pl
abc\#$def
abc\#$def
$
This was tested with Perl 5.14.1 on MacOS X 10.6.8, but I don't think the behaviour would vary with any other version of Perl 5.
Given this, can you update your question to show a script similar to mine (in particular, with both use strict; and use warnings;) but which produces the result you show?
With something like this:
$element=~s/#/\\#/g;
You have to escape the \
Edit
this code works on my machine as you expect:
use strict;
use warnings;
use Data::Dumper;
my $element='abc#$def';
my #arr;
$element=~s/#/\\#/g;
print $element."\n";
push(#arr,$element);
foreach my $val (#arr)
{
print $val;
}