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.
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) {
...
}
#values = (1..5);
foreach $s(#values){
print "$s\n"; #It works
}
print "$_\n",foreach (#values); #It also works
print "$s\n",foreach $s(#values); #It not works
How to give the variable name?
Above the code does not print. It show the syntax error.
How to give the name for the foreach concept in , separated syntax. How can i do it.?
It is not possible to assign a variable name to the for loop iterator when using a statement modifier:
Statement Modifiers
Any simple statement may optionally be followed by a SINGLE modifier, just before the terminating semicolon (or block ending). The possible modifiers are:
...
for LIST
foreach LIST
...
The for(each) modifier is an iterator: it executes the statement once for each item in the LIST (with $_ aliased to each item in turn).
print "Hello $_!\n" for qw(world Dolly nurse);
Instead, as stated above, you must use the $_ variable:
#values = (1..5);
print "$_\n" for #values;
If you want to use a variable name for the iterator, then simply use a for(each) in long form:
for my $var (#values) {
print "$var\n";
}
my #values = (1..5);
print "$_\n" foreach (#values);
__END__
1
2
3
4
5
If you really want the variable to be named $s, you can do this:
my #values = (1..5);
print "$s\n" while($s = shift #values);
but the first approach is more idiomatic and readable.
The other answers are correct, you can't declare a name for the iterator in a for statement modifier. But here is a way you can get the similar effect of doing a loop on one line with a named variable.
perl5i provides a number of language enhancements, including this one...
use perl5i::2;
#a->foreach(func($s) { say $s });
# Similar to...
print "$_\n" for #a;
This also works for grep, map and the notoriously tricky to use correctly "each".
%h->each(func($key, $val) { say "$key => $val" });
You can also work in pairs, triplets, etc...
#a->foreach(func($this,$that) { say "$this, $that" });
Performance may suffer as perl5i makes a full function call for each iteration, rather than just a cheaper block entry.
Although I wouldn't recommend this in general due to readability issues, you could temporarily alias another global variable to the _ variable.
{
our $s;
local *s = *_;
print "$s\n" for 1, 2, 3;
}
You use a do block and reassign the $_ variable inside the block:
#values = (1..5);
do { $s = $_; print "$s\n" },foreach (#values);
The below one the code.
sub max
{
if (#_[0] > #_[1])
{
#_[0];
}
else
{
#_[1];
}
}
print "biggest is ".&max(37,25);
When I ran it, I got the following warnings,
Scalar values #_[0] better written as $_[0] at file.pl line 3.
Scalar values #_[1] better written as $_[1] at file.pl line 3.
Scalar values #_[0] better written as $_[0] at file.pl line 5.
Scalar values #_[0] better written as $_[0] at file.pl line 9.
biggest is 37.
Although I got a correct output, but I wonder what could be the reason behind this warning, Since I think that using #_ in a subroutine would be apropriate than $_.
The problem is that you are referring to your single array element by using an array slice instead of a scalar. Just like the error says. An array slice is a list of elements from an array, for example:
my #a = (0 .. 9);
print #a[0,3,4]; # prints 034
Conversely, when you refer to a single array element you use the scalar prefix $:
print $a[3]; # prints 3
So when you do
#_[0];
Perl is telling you that the proper way to refer to a scalar value is by not using an array slice, but rather to use the scalar notation:
$_[0];
That is all.
Try to understand it with this example:
#array = (1,2,3); #array is the name of the array and # means that it's an array
print $array[1];
#this will print element at index 1 and you're doing it in scalar context
Similarly,
#_ = (1,2,3); #_ is the name of the array
print $_[1];
#this will print element at index 1 of array _ and again you're doing it in scalar context
You are referring to an array, instead of a scalar. #_[0] means ($_[0]). But perl is kind of clever so it warns You that instead of an explicit single element list You should return a scalar. Here You should use $_[0].
I suggest to use prototype, as now You could call max (1, 2, 3) and the result will be 2, instead of 3. So define as
sub max ($$) { $_[0] > $_[1]) ? $_[0] : $_[1] }
Or better, You can use for undefined number (>=2) of elements. Maybe pointless to call it with 0 or 1 items.
sub max (#) {
return undef if $#_<0;
my $s = shift;
for(#_) { $s = $_ if $_ > $s } $s
}
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.
OK, I have the following code:
use strict;
my #ar = (1, 2, 3);
foreach my $a (#ar)
{
$a = $a + 1;
}
print join ", ", #ar;
and the output?
2, 3, 4
What the heck? Why does it do that? Will this always happen? is $a not really a local variable? What where they thinking?
Perl has lots of these almost-odd syntax things which greatly simplify common tasks (like iterating over a list and changing the contents in some way), but can trip you up if you're not aware of them.
$a is aliased to the value in the array - this allows you to modify the array inside the loop. If you don't want to do that, don't modify $a.
See perldoc perlsyn:
If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the foreach loop index variable is an implicit alias for each item in the list that you're looping over.
There is nothing weird or odd about a documented language feature although I do find it odd how many people refuse check the docs upon encountering behavior they do not understand.
$a in this case is an alias to the array element. Just don't have $a = in your code and you won't modify the array. :-)
If I remember correctly, map, grep, etc. all have the same aliasing behaviour.
As others have said, this is documented.
My understanding is that the aliasing behavior of #_, for, map and grep provides a speed and memory optimization as well as providing interesting possibilities for the creative. What happens is essentially, a pass-by-reference invocation of the construct's block. This saves time and memory by avoiding unnecessary data copying.
use strict;
use warnings;
use List::MoreUtils qw(apply);
my #array = qw( cat dog horse kanagaroo );
foo(#array);
print join "\n", '', 'foo()', #array;
my #mapped = map { s/oo/ee/g } #array;
print join "\n", '', 'map-array', #array;
print join "\n", '', 'map-mapped', #mapped;
my #applied = apply { s/fee//g } #array;
print join "\n", '', 'apply-array', #array;
print join "\n", '', 'apply-applied', #applied;
sub foo {
$_ .= 'foo' for #_;
}
Note the use of List::MoreUtils apply function. It works like map but makes a copy of the topic variable, rather than using a reference. If you hate writing code like:
my #foo = map { my $f = $_; $f =~ s/foo/bar/ } #bar;
you'll love apply, which makes it into:
my #foo = apply { s/foo/bar/ } #bar;
Something to watch out for: if you pass read only values into one of these constructs that modifies its input values, you will get a "Modification of a read-only value attempted" error.
perl -e '$_++ for "o"'
the important distinction here is that when you declare a my variable in the initialization section of a for loop, it seems to share some properties of both locals and lexicals (someone with more knowledge of the internals care to clarify?)
my #src = 1 .. 10;
for my $x (#src) {
# $x is an alias to elements of #src
}
for (#src) {
my $x = $_;
# $_ is an alias but $x is not an alias
}
the interesting side effect of this is that in the first case, a sub{} defined within the for loop is a closure around whatever element of the list $x was aliased to. knowing this, it is possible (although a bit odd) to close around an aliased value which could even be a global, which I don't think is possible with any other construct.
our #global = 1 .. 10;
my #subs;
for my $x (#global) {
push #subs, sub {++$x}
}
$subs[5](); # modifies the #global array
Your $a is simply being used as an alias for each element of the list as you loop over it. It's being used in place of $_. You can tell that $a is not a local variable because it is declared outside of the block.
It's more obvious why assigning to $a changes the contents of the list if you think about it as being a stand in for $_ (which is what it is). In fact, $_ doesn't exist if you define your own iterator like that.
foreach my $a (1..10)
print $_; # error
}
If you're wondering what the point is, consider the case:
my #row = (1..10);
my #col = (1..10);
foreach (#row){
print $_;
foreach(#col){
print $_;
}
}
In this case it is more readable to provide a friendlier name for $_
foreach my $x (#row){
print $x;
foreach my $y (#col){
print $y;
}
}
Try
foreach my $a (#_ = #ar)
now modifying $a does not modify #ar.
Works for me on v5.20.2