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

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.

Related

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.

Best way to alternate a variable value in loop? (Perl)

my #arr = (1,2,3,4,5,6,7,8,9);
my $counter = 0;
foreach my $a (#arr) {
my $str;
if ($counter % 2 == 0) {
$str = 'hi';
} else {
$str = 'bye';
}
print $str . "\n";
$counter++;
}
What is the best way to alternate between two different values for each iteration of a while loop? Simple example above, is there a better way than keeping a counter and modding to find even values?
my #arr = qw( a b c );
for my $i (0..$#arr) {
print "$arr[$i] ", $i % 2 ? 'bye' : 'hi', "\n";
}
Good name.
my /*boolean*/ $even = 0;
foreach ...
next if ... # skipping line
$even = !$even;
... # work
print $even ? 'hi' : 'bye';
I'd do like this:
...
my #arr = (1..9);
for (my $i=0; $i<scalar(#arr); $i++) {
my $str = ($i % 2 == 0) ? 'hi' : 'bye';
print "$str\n";
}
my ($foo, $bar) = qw(foo bar);
for (0..10) {
print "$foo\n";
($foo, $bar) = ($bar, $foo);
}
A weird one, but no counters and no modding to find even values:
use strict;
sub foo {
shift || return and bar(#_);
print "doing something with Value2 \n";
}
sub bar {
shift || return and foo(#_);
print "doing something with Value1 \n";
}
foo(1..100);
Do whatever you want by calling foo() or bar() and chosing an even or odd values insted of 100. Maybe you would like to use $#arr insted of hardcoded values and so on.
But please don't write code like this.
Couldn't resist:
my #arr = (1,2,3,4,5,6,7,8,9);
my #alt = qw( hi bye );
my $counter = 0;
foreach my $a (#arr) {
print $alt[($counter++ % 2)] . "\n";
}
Or per dolmen's comment using "&" which should be more efficient compared to modulo (%)
my #arr = (1,2,3,4,5,6,7,8,9);
my #alt = qw( hi bye );
my $counter = 0;
foreach my $a (#arr) {
print $alt[($counter++ & 1)] . "\n";
}

Uninitialized variable issue in Perl program

#!/usr/bin/perl
use warnings;
use Scalar::Util qw(looks_like_number);
sub term_value();
sub factor_value();
sub expression_value()
{
$num = #_;
#expression = $_[0];
print "expression[0]: " . $expression[0] . "\n";
$index = $_[$num-1];
print "index: $index\n";
$result = &term_value(#expression, $index);
$more = 1;
while($more)
{
$op = $expression[$index];
print "$op\n";
if ($op eq "+" || $op eq "-")
{
$index++;
$value = &term_value(#expression, $index);
if ($op eq '+')
{
$result = $result + $value;
} else {
$result = $result - $value;
}
}
else
{
$more = 0;
}
}
return $result;
}
sub term_value()
{
$num = #_;
#expression = $_[0];
print "expression[0]: " . $expression[0] . "\n";
$index = $_[$num-1];
print "index: $index\n";
$result = &factor_value(#expression, $index);
$more = 1;
while($more)
{
$op = $expression[$index];
if ($op eq "*" || $op eq "/")
{
$index++;
$value = &factor_value(#expression, $index);
if ($op eq '*')
{
$result = $result * $value;
} else {
$result = $result / $value;
}
} else {
$more = 0;
}
}
return $result;
}
sub factor_value()
{
$num = #_;
#expression = $_[0];
print "expression[0]: " . $expression[0] . "\n";
$index = $_[$num-1];
print "index: $index\n";
$result = 0;
$c = $expression[$index];
if ($c eq '(')
{
$index++;
$result = &expression_value(#expression, $index);
$index++;
} else {
while (looks_like_number($c))
{
$result = 10 * $result + $c - '0';
$index++;
$c = $expression[$index];
}
}
return $result;
}
#Collect argument and separate by character
#one_char = split(//, $ARGV[0]);
$index = 0;
$result = &expression_value(#one_char, $index);
print $result . "\n";
My console returns these warnings:
Use of uninitialized value $op in string eq at eval.pl line 58.
Use of uninitialized value $op in string eq at eval.pl line 58.
Use of uninitialized value $op in string eq at eval.pl line 25.
Use of uninitialized value $op in string eq at eval.pl line 25.
about the $op variable being uninitialized. I'm thinking this may be a scope problem...but I can't figure it out. I've tried everything I could think of (initializing the variable outside of the loop, etc.), but none of it seems to make a difference when running the program. Any suggestions would be greatly appreciated!
You're only using package (~global) variables, which is a huge problem given that you are using recursive functions! Start by adding
use strict;
Primarily, this will identify the variables you haven't declared. Use my to declare them in the appropriate scope.
You're trying to pass arrays to the subs, but you're failing. The only thing that can be passed to a sub is a list of scalars. If you want to pass an array to a sub, you'll need to pass a reference (~pointer) to the array.
sub foo {
my ($expressions, $index) = #_;
print($expressions->[$index], "\n");
}
foo(\#expressions, $index);
This is the reason you're getting the warnings. You are assigning one element to an array (#expression = $_[0]), then you try to index the second or later element.
By using prototype (), you're telling Perl the sub takes no arguments. Then you use & to tell Perl to ignore the prototype so you can pass arguments to your subs. Get rid of both the () after the sub names and & before sub calls.
my $more = 1;
while ($more) {
...
if (cond) {
...
} else {
$more = 0;
}
}
can be reduced to
while (1) {
...
last if !cond;
...
}
Higher Order Perl has a chapter on parsing. See section 8.1.2 for how you would build an expression parser and evaluator from scratch.
You can also take a look at the demo calculator script provided with Parse::RecDescent.
Just out of curiosity, I wanted to see what can be achieved without using parsers. The following script makes a lot of assumptions, but "works" for the simple cases.
#!/usr/bin/env perl
use strict;
use warnings;
use Regexp::Common qw(balanced number);
die "Need expression\n" unless #ARGV;
my ($expression) = #ARGV;
my $result = evaluate_expression($expression);
printf(
"'%s' evaluated to %g\n",
$expression, $result
);
my $expected = eval $expression;
unless ($result == $expected) {
die "Wrong result, should have been '$expected'\n";
}
sub evaluate_expression {
my ($expression) = #_;
my $n = qr!$RE{num}{real}!;
my $mul = qr![*/]!;
my $add = qr![+-]!;
my $subexpr = qr!$RE{balanced}{-parens=>'()'}{-keep}!;
1 while
$expression =~ s!
$subexpr
!
my $s = $1;
$s =~ s{(?:^\()|(?:\)\z)}{}g;
evaluate_expression($s)
!gex;
1 while
$expression =~ s!($n) \s* ($mul) \s* ($n)!"$1 $2 $3"!geex;
1 while
$expression =~ s!($n) \s* ($add) \s* ($n)!"$1 $2 $3"!geex;
return $expression;
}
Output:
C:\Temp> z "((1+1)*3 +2)*5"
'((1+1)*3 +2)*5' evaluated to 40
C:\Temp> z "(1+1)*3 + 2*5"
'(1+1)*3 + 2*5' evaluated to 16
But, of course, it's fragile:
C:\Temp> z "2*3+2*5"
'2*3+2*5' evaluated to 610
Wrong result, should have been '16'
As a bit of a corollary to Sinan's answer, here is a "parser" written from the other side of the camel.
use 5.010;
use strict;
use warnings;
my #ops;
use overload map {
my $op = $_;
$op => sub {
my ($x, $y) = #_[$_[2] ? (1, 0) : (0, 1)];
bless [$x, $op, $y]
}
} #ops = qw(+ - / *);
my %ops = map {$_ => eval "sub {\$_[0] $_ \$_[1]}"} #ops;
sub eval {
my $self = shift;
return $$self[0] if #$self == 1;
my ($x, $op, $y) = map {ref eq 'main' ? $_->eval : $_} #$self;
my $ret = $ops{$op}->($x, $y);
say "$ret = $x $op $y";
$ret;
}
BEGIN {overload::constant integer => sub {bless [$_[1]]}}
eval->eval for "#ARGV";
Which when run:
$ perl eval.pl 2*3+2*5
prints:
6 = 2 * 3
10 = 2 * 5
16 = 6 + 10

Why doesn't my decrypt function work?

This was not written by me; it was written by someone who passed it down to me. I lost contact with the author of the code. I have been using this code for a few years and just now realized this error. It seems that the letter sequence rkey1 messes up the output.
For example turkey1 outputs as decryption as tur79y1. This Perl code should output turkey1 and not tur79y1:
$String = "turkey1";
$e = &encode_escaped(&palace_encrypt($String));
$d = &palace_decrypt(&decode_escaped("'\"".$e."\"'"));
print $d."<br>\n";
KEY REMOVED BY OWNER
sub palace_decrypt
{
local $lastchar = 0;
local $rc = 0;
local #bs;
for($i=length($_[0])-1; $i>=0; $i--) {
local $tmp = ord(substr($_[0], $i, 1));
$bs[$i] = $tmp ^ $palace_key[$rc++] ^ $lastchar;
$lastchar = $tmp ^ $palace_key[$rc++];
}
return join("", map { chr($_) } #bs);
}
sub decode_escaped
{
$_[0] =~ m/\"(.*)\"/;
local $str = $1;
$str =~ s/\\\\/\0/g;
$str =~ s/\\"/"/g;
$str =~ s/\\(..)/pack("c",hex($1))/ge;
$str =~ s/\0/\\/g;
return $str;
}
sub palace_encrypt
{
local $lastchar = 0;
local $rc = 0;
local #bs;
for($i=length($_[0])-1; $i>=0; $i--) {
local $b = ord(substr($_[0], $i, 1));
$bs[$i] = $b ^ $palace_key[$rc++] ^ $lastchar;
$lastchar = $bs[$i] ^ $palace_key[$rc++];
}
return join("", map { chr($_) } #bs);
}
sub encode_escaped
{
local $str = $_[0];
$str =~ s/\\/\\\\/g;
$str =~ s/([^A-Za-z0-9\.\\])/sprintf("\\%2.2X", ord($1))/ge;
return $str;
}
Your problem is that your decode_escaped does not exactly undo what encode_escaped did. Replace it with the following and that should fix your problem.
sub decode_escaped
{
$_[0] =~ m/\"(.*)\"/;
local #str = split /(\\\\)/, $1;
foreach (#str) {
s/\\"/"/g;
s/\\(..)/chr(hex($1))/ge;
s/\\\\/\\/;
}
return join '', #str;
}

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;