How to access a local variable with in a foreach loop outside of its scope? - perl

I have two arrays of string data type and i am comparing those using foreach loops and raising the counter when a match is found
my #array1 = ('India');
my #array2 = ('India', 'America');
foreach my $a (#array1) {
foreach my $b (#array2) {
my $count=0;
if($a eq $b) {
$count++;
}
}
}
Now I want to use this count variable outside of its scope
if ($count > 0) {
call_some_function();
}
Where am I going wrong?

$count is declared into the foreach loop, it doesn't exist outside this loop, if you want to use $count outside the loop, simply put my $count=0 before the first foreach and remove the one into the foreach
here's what you want:
my #array1=('India');
my #array2=('India','America');
my $count=0;
foreach my $country1(#array1)
{
foreach my $country2(#array2)
{
if($country1 eq $country2)
{
$count++;
}
}
}

Declare variable outside of the loops, you've also got a typo in $count:
my $count = 0;
foreach $a (#array1) {
foreach $b (#array2) {
if ( $a eq $b ) {
$count++;
}
}
}

Related

powershell double for loop

Need advice on loop
$Variable contains 11111 22222
foreach ($variable in $value) {
for ([byte]$c = [char]'b'; $c -le [char]'c'; $c++) {
$variable."([char]$c)" } }
I am looking output as 11111b and then 22222c but currently, I am getting 11111b , 11111c and then 22222b and then 22222c.
Kindly advice
I am assuming you mean that $value, not $variable, contains 11111 and 22222, specifically in an array.
Since you want $c to maintain its value between iterations of the foreach loop you need to initialize $c outside of the foreach loop. Therefore, you really don't need (or, rather, should not use) two loops at all.
$value = 11111, 22222;
[Byte] $c = [Char] 'b';
foreach ($variable in $value)
{
"$variable$([Char] $c++)"
}
This gives the output you are seeking:
11111b
22222c

perl variable not storing data outside block

I have written below mention code to read a file and and storing data to array #s_arr.
But when I am trying to print that #s_arr array outside the block it shows nothing.
use Data::Dumper;
my #s_arr;
my #err;
my %sort_h_1;
$fname = '/qv/Error.log';
open( IN, "<$fname" );
foreach $line ( <IN> ) {
if ( $line =~ /CODE\+(\w{3})(\d{5})/ ) {
$a = "$1$2";
push #err, $a;
}
}
close IN;
$prev = "";
$count = 0;
my %hash;
foreach ( sort #err ) {
if ( $prev ne $_ ) {
if ( $count ) {
$hash{$prev} = $count;
}
$prev = $_;
$count = 0;
}
$count++;
}
print Dumper \%hash;
printf( "%s:%d\n", $prev, $count ) if $count;
$hash{$prev} = $count;
my $c = 0;
print "Today Error Count\n";
foreach my $name ( sort { $hash{$b} <=> $hash{$a} } keys %hash ) {
#printf "%-8s %s\n", $name, $hash{$name};
#my %sort_h ;
push #s_arr, $name;
push #s_arr, $hash{$name};
#$sort_h{$name} = $hash{$name} ;
#print Dumper \%sort_h ;
#print Dumper \#s_arr ;
$c++;
if ( $c eq 30 ) {
exit;
}
}
print Dumper \#s_arr; # It's showing nothing
You are calling exit inside of your foreach loop. That makes the program stop, and the print Dumper #s_arr is never reached.
To break out of a loop you need to use last.
foreach my $name ( sort ... ) {
# ...
$c++;
last if $c == 30; # break out of the loop when $c reaches 30
}
I used the postfix variant of if here because that makes it way easier to read. Also note that as zdim pointed out above, you should use the numerical equality check == when checking for numbers. eq is for strings.

the analysis of my $file = ${$chainro->{$ro}->{$id}}[$i];

I have a two level hash %chainro , each key of $chainro{$ro}{$id}points to an array. The following code is to iterate through the first level of hash, $chainro->{$ro}. I can guess what
does my $file = ${$chainro->{$ro}->$id}}[$i]; aim to perform. However, I do not know why ${$chainro->{$ro}->{$id}}was written this way? In specific, why do we need to add ${ } to wrap the $chainro->${ro}->{$id}
foreach my $id (keys %{$chainro->{$ro}})
{
$size = $#{$chainro->{$ro}->{$id}};
for ($i=0; $i<$size; $i++)
{
my $file = ${$chainro->{$ro}->{$id}}[$i];
}
}
${ EXPR1 }[ EXPR2 ]
is an alternate way of writing
EXPR1->[ EXPR2 ]
so
${ $chainro->{$ro}->{$id} }[$i]
can be written as
$chainro->{$ro}->{$id}->[$i]
or even as
$chainro->{$ro}{$id}[$i]
Cleaned up:
for my $id (keys %{ $chainro->{$ro} }) {
my $files = $chainro->{$ro}{$id};
for my $i (0..$#$files) {
my $file = $files->[$i];
...
}
}
Or if you don't need $i:
for my $id (keys %{ $chainro->{$ro} }) {
my $files = $chainro->{$ro}{$id};
for my $file (#$files) {
...
}
}
It is to dereference a reference to something.
The something is an array here.

Where does $_ come from in this Perl foreach loop?

I found this in Mail::IMAPClient. Where does the $_ in $SEARCH_KEYS{ uc($_) } come from?
sub _quote_search {
my ( $self, #args ) = #_;
my #ret;
foreach my $v (#args) {
if ( ref($v) eq "SCALAR" ) {
push( #ret, $$v );
}
elsif ( exists $SEARCH_KEYS{ uc($_) } ) {
push( #ret, $v );
}
elsif ( #args == 1 ) {
push( #ret, $v ); # <3.17 compat: caller responsible for quoting
}
else {
push( #ret, $self->Quote($v) );
}
}
return #ret;
}
That looks to me like a typo where the author converted an anonymous for loop foreach (#args) to one with an explicit iterator variable foreach my $v (#args) and forgot to convert all the incidences of $_ to $v.
You should probably file a bug against the distribution on CPAN.
Even though this is probably a bug, lets consider how this code behaves.
The value of $_ will be determined by the current dynamic scope. What this means is that $_ will have whatever value (the dynamically scoped copy of) $_ has in the calling subroutine.
So for example if I have:
for (1 .. 5 ) {
foo();
bar();
}
sub foo {
print "\$_ = $_\n";
}
sub bar {
for ( 'a' .. 'c' ) {
foo();
}
}
You get output like:
$_ = 1
$_ = a
$_ = b
$_ = c
$_ = 2
$_ = a
$_ = b
$_ = c
...
It gets a little weirder in Perl 5.10 and up, where a lexical $_ exists.
for (1 .. 5 ) {
foo();
bar();
}
sub foo {
print "\$_ = $_\n";
}
sub bar {
my $_;
for ( 'a' .. 'c' ) {
foo();
}
}
Run this and get:
$_ = 1
$_ = 1
$_ = 1
$_ = 1
$_ = 2
$_ = 2
$_ = 2
$_ = 2
As you can see, if this isn't a bug, it's probably a bad idea.

Why does perl "hash of lists" do this?

I have a hash of lists that is not getting populated.
I checked that the block at the end that adds to the hash is in fact being called on input. It should either add a singleton list if the key doesn't exist, or else push to the back of the list (referenced under the right key) if it does.
I understand that the GOTO is ugly, but I've commented it out and it has no effect.
The problem is that when printhits is called, nothing is printed, as if there are no values in the hash. I also tried each (%genomehits), no dice.
THANKS!
#!/usr/bin/perl
use strict;
use warnings;
my $len = 11; # resolution of the peaks
#$ARGV[0] is input file
#$ARGV[1] is call number
# optional -s = spread number from call
# optional -o specify output file name
my $usage = "see arguments";
my $input = shift #ARGV or die $usage;
my $call = shift #ARGV or die $usage;
my $therest = join(" ",#ARGV) . " ";
print "the rest".$therest."\n";
my $spread = 1;
my $output = $input . ".out";
if ($therest =~ /-s\s+(\d+)\s/) {$spread = $1;}
if ($therest =~ /-o\s+(.+)\s/) {$output = $1;}
# initialize master hash
my %genomehits = ();
foreach (split ';', $input) {
my $mygenename = "err_naming";
if ($_ =~ /^(.+)-/) {$mygenename = $1;}
open (INPUT, $_);
my #wiggle = <INPUT>;
&singlegene(\%genomehits, \#wiggle, $mygenename);
close (INPUT);
}
&printhits;
#print %genomehits;
sub printhits {
foreach my $key (%genomehits) {
print "key: $key , values: ";
foreach (#{$genomehits{$key}}) {
print $_ . ";";
}
print "\n";
}
}
sub singlegene {
# let %hash be the mapping hash
# let #mygene be the gene to currently process
# let $mygenename be the name of the gene to currently process
my (%hash) = %{$_[0]};
my (#mygene) = #{$_[1]};
my $mygenename = $_[2];
my $chromosome;
my $leftbound = -2;
my $rightbound = -2;
foreach (#mygene) {
#print "Doing line ". $_ . "\n";
if ($_ =~ "track" or $_ =~ "output" or $_ =~ "#") {next;}
if ($_ =~ "Step") {
if ($_ =~ /chrom=(.+)\s/) {$chromosome = $1;}
if ($_ =~ /span=(\d+)/) {$1 == 1 or die ("don't support span not equal to one, see wig spec")};
$leftbound = -2;
$rightbound = -2;
next;
}
my #line = split /\t/, $_;
my $pos = $line[0];
my $val = $line[-1];
# above threshold for a call
if ($val >= $call) {
# start of range
if ($rightbound != ($pos - 1)) {
$leftbound = $pos;
$rightbound = $pos;
}
# middle of range, increment rightbound
else {
$rightbound = $pos;
}
if (\$_ =~ $mygene[-1]) {goto FORTHELASTONE;}
}
# else reinitialize: not a call
else {
FORTHELASTONE:
# typical case, in an ocean of OFFs
if ($rightbound != ($pos-1)) {
$leftbound = $pos;
}
else {
# register the range
my $range = $rightbound - $leftbound;
for ($spread) {
$leftbound -= $len;
$rightbound += $len;
}
#print $range . "\n";
foreach ($leftbound .. $rightbound) {
my $key = "$chromosome:$_";
if (not defined $hash{$key}) {
$hash{$key} = [$mygenename];
}
else { push #{$hash{$key}}, $mygenename; }
}
}
}
}
}
You are passing a reference to %genomehits to the function singlegene, and then copying it into a new hash when you do my (%hash) = %{$_[0]};. You then add values to %hash which goes away at the end of the function.
To fix it, use the reference directly with arrow notation. E.g.
my $hash = $_[0];
...
$hash->{$key} = yadda yadda;
I think it's this line:
my (%hash) = %{$_[0]};
You're passing in a reference, but this statement is making a copy of your hash. All additions you make in singlegene are then lost when you return.
Leave it as a hash reference and it should work.
PS - Data::Dumper is your friend when large data structures are not behaving as expected. I'd sprinkle a few of these in your code...
use Data::Dumper; print Dumper \%genomehash;