compilation error while defining a variable scope - perl

Could someone kindly point me why this snippet is not compiled:
my $crond = "/etc/init.d/crond";
if( -e $crond ) {
my $d = "d";
}
my $crond = "/etc/init.d/cron$d";
Error:
"my" variable $crond masks earlier declaration in same scope at /home/andrew/sandbox/processes2cron.pl line 27.
Global symbol "$d" requires explicit package name at /home/andrew/sandbox/processes2cron.pl line 27.
I tried different variations with 'my' but still the scope is defined uncorrectly. Thanks.

You have already declared the variable $crond in the first line of your code. By re-declaring it on line 5, you will lose the previous value. In this case, removing the my on line 5 will stop the warning.
The variable $d is declared within the scope of the if block. This means that it is only accessible until the end of the if block. You then try to refer to it outside the if block, which causes the error. To fix this, declare $d before the if statement in the outer scope.

my $crond = "/etc/init.d/crond";
my $d;
if( -e $crond ) {
$d = "d";
}
$crond = "/etc/init.d/cron$d";
It's just as the error message says. You're redeclaring $cron within the same scope, and $d is only defined within the if block, so the compiler expects $d to be a global variable when you use it on the last line, and complains when it can't find it.

Related

Deciphering this syntax error

I cant seem to understand the reason for these syntax errors. The following is part of my code. I have strict and warnings implemented.
my $res = $s->scrape(URI->new($urlToScrape));
#warn Dump $res;
print "Show :".$res->{showtitle}[0];
my #sct;
if ( defined {season}[0] ) {
print $res->{season}[0];
#sct=split(' ', $res->{season}[0]);
} else {
my #spaa=split( {showtitle}[0], {fulltitle}[0] );
print "Couldnt find season num at proper position\n";
print $spaa[0]."\n";
print $spaa[1]."\n";
exit;
}
The error I get is:
$ ./htmlscrape.pl
"my" variable #spaa masks earlier declaration in same scope at ./htmlscrape.pl line 43.
"my" variable #spaa masks earlier declaration in same scope at ./htmlscrape.pl line 44.
syntax error at ./htmlscrape.pl line 37, near "}["
syntax error at ./htmlscrape.pl line 40, near "}"
syntax error at ./htmlscrape.pl line 46, near "}"
Execution of ./htmlscrape.pl aborted due to compilation errors.
There's syntax error in your code. Change:
if ( defined {season}[0] )
to
if ( defined $res->{season}[0] )
and
my #spaa=split( {showtitle}[0], {fulltitle}[0] );
to
my #spaa=split( $res->{showtitle}[0], $res->{fulltitle}[0] );
Also you are getting the warning
"my" variable #spaa masks earlier declaration in same scope at ./htmlscrape.pl line 43.
That means you have declared two arrays with the same name #spaa in same scope. You'll probably find Coping with Scoping by Dominus worth reading. Pay particular attention to the section called "Lexical Variables".

What is the correct way to fix "variable ... will not stay shared at ..."?

I'm trying to use this tool. (Perl version) However, when I try to run it using the recommended command perl bin/SWOG.pl --input=examples/simple.swog --toPng=simple, it displays the following warning (added use diagnostics hoping it would shed some light on how to fix it)
Variable "$np" will not stay shared at (re_eval 8) line 2 (#1)
(W closure) An inner (nested) named subroutine is referencing a
lexical variable defined in an outer named subroutine.
When the inner subroutine is called, it will see the value of
the outer subroutine's variable as it was before and during the first
call to the outer subroutine; in this case, after the first call to the
outer subroutine is complete, the inner and outer subroutines will no
longer share a common value for the variable. In other words, the
variable will no longer be shared.
This problem can usually be solved by making the inner subroutine
anonymous, using the sub {} syntax. When inner anonymous subs that
reference variables in outer subroutines are created, they
are automatically rebound to the current values of such variables.
I've done my due diligence to Google: link, but still do not understand how to apply this in my case.
I've also went back to the source of the code snippet causing this issue. The snippet is produced again below for easy reference:
# parentheses balance pattern
# # http://www.unix.org.ua/orelly/perl/prog3/ch05_10.htm
$np= qr{
\(
(
(?:
(?> [^()]+ ) # Non-parens without backtracking
|
(??{ $np }) # Group with matching parens
)*
)
\)
}x;
I am of the opinion that the nested $np within the definition of this same variable $np is causing this warning.
Please help. Thanks!
You have something like
sub f {
my $np;
$np = qr/...(??{ $np }).../;
}
(??{...}) captures the lexical therein when the pattern is compiled.
In your case, because the pattern is constant, the regex pattern in the qr// is compiled when the qr// itself is compiled. Unfortunately, a new $np is be created every time the function runs.
You can fix the problem by avoiding lexical variables.
sub f {
local our $np;
$np = qr/...(??{ $np }).../;
... /$np/ ...
}
Or by forcing the regex pattern to be compiled when the qr// is executed by making the pattern it variable.
sub f {
my $var = '';
my $np;
$np = qr/...(??{ $np })...$var/;
... /$np/ ...
}
But why execute qr// repeatedly for a constant pattern? The best solution is to move the pattern out of the sub.
my $np;
$np = qr/...(??{ $np }).../;
sub f {
... /$np/ ...
}

Perl magic -- Is foreach variable by default locally defined and override outer value

I am expecting #arr1's last element as output from this code:
#!/usr/bin/perl
my #arr1 = qw(son kon bon won kon don pon won pon don won);
my $innr_element = '';
foreach $innr_element ( #arr1 ) {
## do something
}
print "--->$innr_element<---\n";
But I am getting nothing (blank output). If $innr_element is being created internally by Perl as block-scoped variable (inner to foreach) then below should work properly.
#!/usr/bin/perl
use strict;
my #arr1 = qw(son kon bon won kon don pon won pon don won);
#my $innr_element = '';
foreach $innr_element ( #arr1 ) {
##do something
}
print "--->$innr_element<---\n";
But above code return below error.
Global symbol "$innr_element" requires explicit package name at test.pl line 5.
Global symbol "$innr_element" requires explicit package name at test.pl line 8.
Execution of test.pl aborted due to compilation errors.
So its clear that Perl is not creating inner variable implicitly.
This document says the same. If you declare VAR with my, the scope of the variable will extend throughout the foreach statement, but not beyond it.
Is this another perl magic or I am missing something?
This is not described in much detail in the documentation, but here is what it says:
The foreach loop iterates over a normal list value and sets the variable VAR to be each element of the list in turn. If the variable is preceded with the keyword my, then it is lexically scoped, and is therefore visible only within the loop. Otherwise, the variable is implicitly local to the loop and regains its former value upon exiting the loop. If the variable was previously declared with my, it uses that variable instead of the global one, but it's still localized to the loop. This implicit localization occurs only in a foreach loop.
So, it is not magic, the variable is merely localized. Your example might look something like this:
my $innr_element='';
foreach local $innr_element ( #arr1 ) {
...

Why does this Perl variable keep its value

What is the difference between the following two Perl variable declarations?
my $foo = 'bar' if 0;
my $baz;
$baz = 'qux' if 0;
The difference is significant when these appear at the top of a loop. For example:
use warnings;
use strict;
foreach my $n (0,1){
my $foo = 'bar' if 0;
print defined $foo ? "defined\n" : "undefined\n";
$foo = 'bar';
print defined $foo ? "defined\n" : "undefined\n";
}
print "==\n";
foreach my $m (0,1){
my $baz;
$baz = 'qux' if 0;
print defined $baz ? "defined\n" : "undefined\n";
$baz = 'qux';
print defined $baz ? "defined\n" : "undefined\n";
}
results in
undefined
defined
defined
defined
==
undefined
defined
undefined
defined
It seems that if 0 fails, so foo is never reinitialized to undef. In this case, how does it get declared in the first place?
First, note that my $foo = 'bar' if 0; is documented to be undefined behaviour, meaning it's allowed to do anything including crash. But I'll explain what happens anyway.
my $x has three documented effects:
It declares a symbol at compile-time.
It creates an new variable on execution.
It returns the new variable on execution.
In short, it's suppose to be like Java's Scalar x = new Scalar();, except it returns the variable if used in an expression.
But if it actually worked that way, the following would create 100 variables:
for (1..100) {
my $x = rand();
print "$x\n";
}
This would mean two or three memory allocations per loop iteration for the my alone! A very expensive prospect. Instead, Perl only creates one variable and clears it at the end of the scope. So in reality, my $x actually does the following:
It declares a symbol at compile-time.
It creates the variable at compile-time[1].
It puts a directive on the stack that will clear[2] the variable when the scope is exited.
It returns the new variable on execution.
As such, only one variable is ever created[2]. This is much more CPU-efficient than then creating one every time the scope is entered.
Now consider what happens if you execute a my conditionally, or never at all. By doing so, you are preventing it from placing the directive to clear the variable on the stack, so the variable never loses its value. Obviously, that's not meant to happen, so that's why my ... if ...; isn't allowed.
Some take advantage of the implementation as follows:
sub foo {
my $state if 0;
$state = 5 if !defined($state);
print "$state\n";
++$state;
}
foo(); # 5
foo(); # 6
foo(); # 7
But doing so requires ignoring the documentation forbidding it. The above can be achieved safely using
{
my $state = 5;
sub foo {
print "$state\n";
++$state;
}
}
or
use feature qw( state ); # Or: use 5.010;
sub foo {
state $state = 5;
print "$state\n";
++$state;
}
Notes:
"Variable" can mean a couple of things. I'm not sure which definition is accurate here, but it doesn't matter.
If anything but the sub itself holds a reference to the variable (REFCNT>1) or if variable contains an object, the directive replaces the variable with a new one (on scope exit) instead of clearing the existing one. This allows the following to work as it should:
my #a;
for (...) {
my $x = ...;
push #a, \$x;
}
See ikegami's better answer, probably above.
In the first example, you never define $foo inside the loop because of the conditional, so when you use it, you're referencing and then assigning a value to an implicitly declared global variable. Then, the second time through the loop that outside variable is already defined.
In the second example, $baz is defined inside the block each time the block is executed. So the second time through the loop it is a new, not yet defined, local variable.

cannot acces variable inside while loop (in perl)

A very easy question on variables scope. I have a variable defined in the main code that I use inside a while loop.
my $my_variable;
while(<FILE>) {
...using $my_variable
}
if ($my_variable) -> FAILS
When I exit the loop and use the variable I get an error:
Use of uninitialized value $my_variable
Even if I enclose the variable in a naked block I follow with the error.
{
my $my_variable;
while(<FILE>) {
...using $my_variable
}
if ($my_variable) -> FAILS
}
Any suggestion?
Are you using $my_variable in the loop or did you redefine it as my $my_variable somewhere in the loop. You'd be surprised how easily a my slips in to variable assignment.
I even have a frequent tick in my code where I write something like
my $hash{ $key } = ... some elaborate assignment;
Also, if should not complain about an uninitialized variable. undef => Boolean false.
I wouldn't worry about initializing all my variables -- but its crucial that you learn a mindset about how to use undefs. And always, always USUW (use strict; use warnings) so that variables aren't uninitialized when you don't expect them to be.
Do you ever assign to $my_variable? All you show is a declaration (the my) and a usage (the if) but never an assignment.
In the examples given, you didn't initialize $my_variable so it is undefined after the while loop.
You can do my $my_variable = ''; just before the loop.