I have a fragment in this form:
my $a = $some_href->{$code}{'A'}; # a number or undef
my $b = $some_href->{$code}{'B'}; # a number or undef
$a = 0 unless defined($a);
$b = 0 unless defined($b);
my $total = $a + $b;
The reality is even more messy, since more than two variables are concerned.
What I really want to write is this:
my $total = $some_href->{$code}{'A'} + $some_href->{$code}{'B'};
and have undef correctly evaluate to 0 but I get these warnings in almost every run:
Use of uninitialized value in addition (+) at Stats.pm line 192.
What's the best way to make these messages go away?
NB: I 'use strict' and 'use warnings' if that s relevant.
It's good that you're using strict and warnings. The purpose of warnings is to alert you when Perl sees behavior that's likely to be unintentional (and thus incorrect). When you're doing it deliberately, it's perfectly fine to disable the warning locally. undef is treated as 0 in numeric contexts. If you're okay with both having undefined values and having them evaluate to zero, just disable the warning:
my $total;
{
no warnings 'uninitialized';
$total = $some_href->{$code}{A} + $some_href->{$code}{B};
}
Note: Disable only the warnings you need to, and do so in the smallest scope possible.
If you're averse to disabling warnings, there are other options. As of Perl 5.10 you can use the // (defined-or) operator to set default values. Prior to that people often use the || (logical-or), but that can do the Wrong Thing for values that evaluate to false. The robust way to default values in pre-5.10 versions of Perl is to check if they're defined.
$x = $y // 42; # 5.10+
$x = $y || 42; # < 5.10 (fragile)
$x = defined $y ? $y : 42; # < 5.10 (robust)
You can turn off the “uninitialized” warning for a second:
my $a;
my $b = 1;
{
no warnings 'uninitialized';
my $c = $a+$b; # no warning
}
my $c = $a+$b; # warning
Or you can short-circuit to zero:
my $d = ($a||0)+$b; # no warning
Doesn’t look very nice to me though.
my $a = $some_href->{$code}{'A'} || 0;
my $b = $some_href->{$code}{'B'} || 0;
my $total = $a + $b;
In this case, it's OK to treat false values the same as undefined values because of your fallback value.
As you are adding them, just filter out the undefs.
use List::Util 'sum';
my $total = sum (0, grep {defined} $some_href->{$code}{'A'}, $some_href->{$code}{'B'});
Or even
use List::Util 'sum';
my $total = sum (0, grep {defined} map {$some_href->{$code}{$_}} 'A', 'B');
To convert undef to 0, use either of $var // 0 or $var //= 0.
The operator is somewhat new (couldn't find when it was introduced), but practically any Perl in use should have it now.
Related
I tried to find the error myself, but don't see it. The following code produces warnings (same problem in Perl 5.18.2 and 5.32.1).
Use of uninitialized value in numeric comparison (<=>) at test.pl line 14.
while the comparison function of the sort is performed (and, as a consequence, the sort operation is not performed correctly). As far as I see, no value of the hash is initial, they all have defined numeric values.
use strict;
use warnings;
# Sort the occurrences of letters in a list of words, highest count first.
my #words = ('ACGT','CCGT','CATG'); # Just an example
my $a = join '',#words;
my $l = length $a;
my %count = ();
for (my $i = 0; $i < $l; $i++) {
my $x = substr( $a, $i, 1);
$count{$x}++;
}
for my $x (sort { $count{$b} <=> $count{$a} } keys %count) {
print "$x: $count{$x}\n";
}
Remark : it didn't help adding the hash element creation line before incrementing it with the $count{$x}++; statement - same result (expectedly, as undef counts like 0 for the increment operation):
...
$count{$x} = 0 unless defined $count{$x};
$count{$x}++;
...
Normally, the sort function uses two package variables called $a and $b to do the sorting. Specifically, sort sets the variables called $a and $b on the current package to be the current sort values. Those are not arguments to your { $count{$b} <=> $count{$a} } block; they're global variables in the current package.
Now, $b is fine. Since you never do anything else with it, Perl picks up the package variable just fine. But you declared a lexical (my) variable called $a earlier in your code, and that lexical is shadowing the package variable.
So sort is setting a variable called, effectively, $YourPackage::a, and your code is accessing a local variable called my $a, which is unrelated to the other one.
You can fix this by changing the my $a = join '',#words; variable to be called something else, and in fact you should probably do that. The names $a, $b, and $_ are used for things like this indiscriminately in Perl for historical reasons, so it's probably best to never have your own variables named any of those names.
But if you don't want to (or can't) change any of the rest of the code, you can expose the package variable with our.
for my $x (sort { our $a; $count{$b} <=> $count{$a} } keys %count) {
...
}
I have this simple code:
#!/usr/bin/perl
#inp = map 2**$_, 0..6;
#cc = grep {
my $num = $inp[$_];
my $sum; #---- HERE, I have to have the var declared first, before init.
$sum += $_ for (split //, $num);
print "$sum\n";
$sum % 2;
} 0..$#inp;
Here, the $sum will be used in for loop, However in this case:
#!/usr/bin/perl
#inp = map 2**$_, 0..6;
#cc = grep {
my $num = $inp[$_];
my $sum += $_ for (split //, $num); # HERE, Trying to autovificate - wont work
print "$sum\n";
$sum % 2;
} 0..$#inp;
But when I used var $sum at the same line with for loop - that means I am trying to declare and initiate at once - where should work the autovivifaction - As i would expect to autovivificate the $sum to zero (because used with math operator +=), but will not work, but why so? What are the rules for autovivification?
This is not autovivification. You have a syntax mistake. If you had use strict and use warnings turned on, it would be more obvious.
The post-fix for construct treats the left-hand side like a block. So there is a scope for the body of the loop. Therefore you are declaring your my $sum inside that loop body scope, and it's not visible outside.
If you turn on use warnings, you'll get Use of uninitialized value $sum in concatenation (.) or string at ... line 6, which is the print after.
You need to declare the variable first (and use strict and warnings!).
my has two effects:
At compile time, my declares the variable.
At run time, my allocates a new variable. More or less.
The first effect is what allows you to refer to the variable until the end of the enclosing block.
The second effect means $sum can't possibly hold the sum at the end of the loop since you call my to create a new variable each pass of the loop.
[ Warning: This section discusses Perl's guts. Feel free to jump ahead. ]
But why is it undef instead of containing the number from the last pass?
Well, that's cause my doesn't actually allocate a new variable when executed. It places an instruction on the stack to allocate a new one on scope exit!
The for statement modifier creates a lexical scope so that $_ can be properly restored when the statement is complete, so my $sum is replaced with a fresh variable at the end of each loop pass. (It's technically only being cleared rather than deallocated and reallocated thanks to an optimization.)
Your code could be written as follows:
#!/usr/bin/perl
use strict;
use warnings;
sub sum { my $acc; $acc += $_ for #_; $acc }
my #inp = map 2**$_, 0..6;
my #cc = grep { ( sum split // ) % 2 } #inp;
or even just
my #cc = grep { ( sum split //, 2**$_ ) % 2 } 0..6;
Always use use strict; use warnings;. Note that use warnings; would have made it more obvious that something was going wrong.
By the way, I don't know what you think autovivification means, but it's wrong.
Autovivification is the creation of a variable and a reference to it when deferencing an undefined value.
$ perl -e'
my $x;
CORE::say $x // "[undef]";
$x->[0] = 123;
CORE::say $x // "[undef]";
'
[undef]
ARRAY(0x35d7f56740)
Less formally, it could also refer to the creation of hash or array elements when using them as lvalues.
$ perl -e'
my $x;
CORE::say exists($h{x}) ? 1 : 0;
my $ref = \( $h{x} );
CORE::say exists($h{x}) ? 1 : 0;
'
0
1
There's no attempt to autovivify in your code.
I'm having a problem with Perl. It's giving me an error that I don't understand.
This is my program
our $neighbor = {};
$$neighbor{'a'} = 'b';
print Dumper($vizinho);
sub keyboard{
my ($a,$b) = #_;
return 4 if ($a eq $b);
return 2 if $neighbor{$a}{$b};
return -1;
}
And my error is
Variable "%vizinho" is not imported at t/DistStr.t line 30.
Global symbol "%vizinho" requires explicit package name at t/DistStr.t line 30.
Execution of t/DistStr.t aborted due to compilation errors.
# Looks like your test exited with 255 just after 1.
What I want to do is
use Alinhamento;
$neighbor = ... # Something which will create an hash where
# $neighbor{$x}{$y} exists if the letter $x is neighbour of $y
sub keyboard {
my ($a, $b) = #_;
return 4 if ($a eq $b);
return 2 if $neighbor{$a}{$b}; #And use that $neighbor on that function
return -1;
}
But I don't know how to make that. May I have some advice please?
There are a number of approaches, depending on specifics that are not apparent in the context provided. Here is one simple approach loosely based on your original posting:
use Data::Dumper;
our %neighbor;
$neighbor{'a'}{'b'} = 1;
print Dumper(\%neighbor);
sub keyboard{
my ($a, $b) = #_;
return 4 if ($a eq $b);
return 2 if $neighbor{$a}{$b};
return -1;
}
EDIT: Renamed $vizinho to %neighbor. Thanks #Borodin.
Note $a and $b are a little special in Perl. See the documentation for sort.
You must start every Perl program with
use strict;
use warnings;
and many problems will resolve themselves.
You use package (global) variables in the same way as any other variable. In fact, if you have been used to not using strict, then all of your variables have probably been package variables.
Update
To set element x, y of your hash so that it exists and is true, you should write
$vizinho->{$x}{$y} = 1;
Your problem seems to be with how to use references. You declare
our $vizinho = {}
and then try to assign to it using
$$vizinho{'a'} = 'b'
which is wrong. You could write
${$vizinho}{'a'} = 'b'
or, much better
$vizinho->{a} = 'b'
You also shouldn't ever use $a or $b. Your code should look like this
use strict;
use warnings;
our $vizinho = {};
$vizinho->{a} = 'b';
sub keyboard {
my ($meu_a, $meu_b) = #_;
return 4 if $meu_a eq $meu_b;
return 2 if $vizinho->{$meu_a} eq $meu_b;
return -1;
}
With the below line you create a scalar to a hash reference:
our $neighbor = {};
What I suspect you want is just a hash
our %neighbor = (
'foo' => {
'bar' => 1,
},
'biz' => {
'baz' => 1,
},
);
sub keyboard {
my ($key1, $key2) = #_;
return 4 if ($key1 eq $key2);
return 2 if $neighbor{$key1}{$key2}; #And use that $neighbor on that function
return -1;
}
Also note that $a and $b are special variables used by perl's sort function, so it's probably best to use another variable name for clarity.
I have the following question:
I want to create a perl script that reads from a text file (file with several columns of numbers) and calculate some statistics (mean, median, sd, variance). I already built one script, but as I am not in love yet with perl, I can't fix the problems of syntax on it...
Here is my perl script..
#!/usr/bin/perl -w
use strict;
open(FILEHANDLE, data.txt);
while (<FILEHANDLE>) {
shift #ARGV;
my #array = split(\t,$_);
}
close(FILEHANDLE);
###### mean, sum and size
$N = $sum = 0;
$array[$x-1];
$N++;
$sum += $array[$x-1];
###### minimum and the maximum
($min = 0, $max = 0);
$max = $array[$x-1] if ($max < $array[$x-1]), (my#sorted = sort { $a <=> $b } #samples) {
print join(" ",#sorted);
}
##### median
if ($N % 2==1) {
print "$median = $sorted[int($N/2)]\n"; ## check this out
};
else ($median = ($sorted[$N/2] + $sorted[($N/2)-1]) / 2)) {
print "$median\n"; # check this out
};
##### quantiles 1º and 3º
if $qt1 = $sorted[$r25-1] {
print "\n"; # check this out
};
else $qt1 = $fr*($sorted[$ir] - $sorted[$ir-1]) + $sorted[$ir-1] {
print "\n"; # check this out
};
##### variance
for (my $i=0;
$i<scalar(#samples);
$i++)
{
$Var += ($samples[$i]-$mean)**2;
$Var = $Var/($N-1);
};
###### standard error
($Std = sqrt($Var)/ sqrt($N));
############################################################
print "$min\n";
print "$max\n";
print "$mean\n";
print "$median\n";
print "$qt1\n";
print "$var\n";
print "$std\n";
exit(0);
I want to get it working. Please help. THANKS IN ADVANCE!
Errors in your code:
open(FILEHANDLE, data.txt);
data.txt needs to be quoted. You are not checking the return value of the open, e.g. ... or die $!. You should use a lexical filehandle and three argument open, e.g. open my $fh, '<', "data.txt" or die $!.
shift #ARGV;
This does nothing except remove the first value from you argument list, which is then promptly discarded.
my #array = split(\t,$_);
You are using \t as a bareword, it should be a regex, /\t/. Your #array is declared inside a lexical scope of the while loop, and will be undefined outside this block.
$N = $sum = 0;
Both variables are not declared, which will cause the script to die when you use strict (which is a very good idea). Use my $N to solve that. Also, $N is not a very good variable name.
$array[$x-1];
This will do nothing. $x is not declared (see above), and also undefined. The whole statement does nothing, it is like having a line 3;. I believe you will get an error such as Useless use of variable in void context.
$N++;
This increments $N to 1, which is a useless thing to do, since you only a few lines above initialized it to 0.
Well.. the list goes on. I suggest you start smaller, use strict and warnings since they are very good tools, and work out the errors one by one. A very good idea would be to make subroutines of your calculations, e.g.:
sub sum {
# code here
return $sum;
}
Go to perldoc.perl.org and read the documentation. Especially useful would be the syntax related ones and perlfunc.
Also, you should be aware that this functionality can be found in modules, which you can find at CPAN.
Your main problem is you have not declared your variables such as $N, $max, etc.
You need to introduce all new variables with my the first time you reference them. Just like you did with $array and $i. So for example
$N = $sum = 0;
Should become
my( $N, $sum ) = ( 0, 0 );
The List::MoreUtils module indicates that you use the variables $a and $b when supplying the BLOCK that goes with the pairwise function. For example:
use strict;
use warnings;
use List::MoreUtils qw'pairwise';
my #x = ( 1 .. 5);
my #y = (11 .. 15);
my #sums = pairwise { $a + $b } #x, #y;
But when I do that, I get warnings like this:
Name "main::b" used only once: possible typo at try.pl line 7.
Name "main::a" used only once: possible typo at try.pl line 7.
Is there an elegant way to deal with this problem?
Update:
See the answer by Ether for perl v5.19.6 and beyond: problem solved.
Depends on what you consider elegant.
no warnings qw(once);
our ($a, $b);
One of these two will suffice. You can even limit their scope pretty easily.
my #sums = pairwise { no warnings qw(once); $a + $b } #x, #y;
my #sums = pairwise { our $a + our $b } #x, #y;
Explicitly specifying the package will suppress the warning too. If you're in main,
my #sums = pairwise { $::a + $::b } #x, #y;
Yep, it's not you. You can no warnings 'once'; or you can predeclare $a and $b so that they will not be used once anymore.
our ($a, $b);
does the trick. I tend to prefer that because it doesn't turn off warnings for anything else, and it's a bit more descriptive.
This is probably a bug in List::Util.
Turning off warnings globally is probably not a good idea, however you could do something like this:
{
no warnings 'once';
return join("_", #monsters) if #monsters && List::Util::reduce { $a && $b // 0 > 0 } 1,#monsters;
}
This would turn off the relevant warning category for that part of the code only.
As of perl 5.19.6, this warning is disabled for all uses of $a and $b everywhere.
Add this near top of your program:
use vars qw( $a $b );
or, if you don't like the "obsolete" part of perldoc vars, simply add:
our ( $a, $b );
I have the same problem with a similar module I'm writing. The only solution I've found (other than using functions that use $a and $b twice, of course) is to put this line somewhere in your code:
$a = $b; # hack to disable warnings about "main::a" used only once
It basically does nothing, but it does disable the warning. Consider keeping the comment so future maintainers don't have to read your mind.