How to re-declare a variable in the same scope in perl? - perl

Is there a way to re-declare a variable in the same scope using the my keyword in perl? When I run the following script:
use warnings;
use strict;
my $var = 3;
print "$var\n";
undef $var;
my $var = 4;
print "$var\n";
I get the "desired" output, but there is also a warning "my" variable $var masks earlier declaration in same scope. Is there a way to re-declare the variable without getting the warning?
I'm not sure, but I think this is because my happens at compile-time and undef happens at run-time because the warning is being printed even before the first print statement. (I'm not even sure if perl actually compiles the thing before running it.)
Context: I want to be able to copy a chunk of code and paste it multiple times in the same file without having to edit-out all the my declarations. I guess this isn't the best way to do it, but any solution to the problem would be appreciated.

To avoid the warning, you can enclose the new variable declaration, and the code that uses it, inside curly braces ({...}) and create a new scope.
my $var = 3;
print "$var\n";
{
my $var = 4;
print "$var\n";
}

Related

Accessing variable outside foreach loop

what is the scope of a variable declared in this way:
foreach $variable (<FILE>){
if($variable...){
}
}
print "$variable \n";
is it possible to use it outside loop?
thanks in advance.
At first, it would seem that it should work, because you're obviously not running this code with strict as you don't declare $variable. And you're not declaring it a lexical variable (my $variable), so it is a "package variable", and works like a global.
However, Perl needlessly, localizes the scope of the loop variable.
And even though this looks like it should work:
use strict;
use warnings;
use feature 'say';
...
my $variable; # creates a lexical variable.
foreach $variable (<FILE>){
if($variable...){
...
}
}
say $variable; # modern form of: print "$variable \n";
Perl needlessly again, localizes the scope of the variable.
Often you can declare the lexical as part of the loop. Like so:
foreach my $variable ( <FILE> ) {
...
}
It does not allow you to access that variable outside of the loop. However, whether you specify my in the loop or not, just putting the variable before the parenthesis localizes the scope of whatever variable you might use.
So if you want to know what the value is outside the loop, it has to be explicitly other than the loop variable.
my $var;
foreach $variable ( <FILE> ) {
$var = $variable;
}
say $var;
In the comments below, you asked me what better way to read a file. So the below contains some nitpicks.
By far the best way to loop through a file is a while loop. It has much less overhead than a foreach loop, and the Perl syntax makes it easy to use.
use English qw<$OS_ERROR>; # imports a standard readable alias for $!
# 1) Use lexical file handles, not "barewords"; 2) use 3-argument open;
# 3) always open or die.
open( my $handle, '<', 'foo.txt' )
or die "Could not open file: $OS_ERROR!"
;
while ( my $line = <$handle> ) {
...
}
close $handle;
Yes, it is possible, but be aware that variable is always localized to the loop (restores previous value after the loop).
From perldoc perlsyn
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.

Why am I *not* getting this warning: "variable masks earlier declaration"?

Here's my entire script, crafted to include two variable with the same name, one of which is masking the other:
#!/usr/bin/env perl
use strict;
use warnings;
my $hi = "First hi";
print "$hi\n";
{
my $hi = "Second hi";
print "$hi\n";
}
print "$hi\n";
If I run this script, I get this output, and noticeably no warnings:
First hi
Second hi
First hi
If I remove the curly braces around the second $hi variable so that is in the same scope as the first $hi variable, I get this warning:
"my" variable $hi masks earlier declaration in same scope at hi.pl
However, I want this warning even when the variable is not in the same scope. I want the warning every time a variable name is shadowing another. How can I enable this warning? Is there a Perl Critic policy that I can enable that will warn me about this?
Did you try this:
Perl::Critic::Policy::Variables::ProhibitReusedNames;

Declaring variables in Perl

I'm learning Perl, and facing some inconsistency between running a program from the command line versus interactively executing it interactively in the debugger.
Specifically, I invoke the Perl debugger with perl -d -e 1, and run this code line-by-line
my $a = 1;
print $a;
$b = 2;
print $b;
In the output I am only seeing the value of $b, while $a seems to be undefined. At the same time, when I execute the same statements with perl myscript.pl, both values are shown in the output. Why does this happen? What am I missing?
The debugger is a wholly different environment from run time Perl. Each line you enter behaves like a separate block, and if you declare a lexical variable like my $a then it will be deleted immediately after the command.
It is as if you had written
{ my $a = 1; }
{ print $a; }
{ $b = 2; }
{ print $b; }
Ordinarily you will declare lexical variables at an appropriate point in the program so that they don't disappear before you need them. But if you want to use the debugger to play with the language then you need to use only package variables, which never disappear and are what you get by default if you don't use my.
Command line "one-liner" Perl programs usually do the same thing, but it's a lesson you will have to unlearn when you come to writing proper Perl programs. You will be using use strict and use warnings at the head of every program, and strict requires that you choose between lexical or package variables by using my or our respectively. If you try to use a variable that you haven't previously declared then your program won't compile.
Also never use $a or $b in your code. Apart from being dreadful variable names, they are reserved for use by the sort operator.
I hope that helps.

Output is not valid for My type declaration

The below code gives some strange output. Can any one explain the problem?
use warnings;
my $P = 10;
print "My Var: $P\n";
display();
my $P = 12;
display();
sub display()
{
print "My Var: $P\n";
}
output:
My Var: 10
My Var:
My Var: 12
You should give the complete output of your script:
"my" variable $P masks earlier declaration in same scope at P line 6.
main::display() called too early to check prototype at P line 4.
main::display() called too early to check prototype at P line 7.
My Var: 10
Use of uninitialized value in concatenation (.) or string at P line 11.
My Var:
My Var: 12
and read it... The answer is in the warning...
and if you add use strict; in the start of your script, you would get the additional warning:
main::display() called too early to check prototype at P line 5.
main::display() called too early to check prototype at P line 8.
Which means that you call a subroutine WITH a prototype (in this case ()) before declaring it...
So declare the subroutine first or, better yet, drop the prototype.
change your code to:
use strict;
use warnings;
my $P = 10;
print "My Var: $P\n";
display();
$P = 12;
display();
sub display
{
print "My Var: $P\n";
}
And it works as you would expect.
(but better use $P as an argument for the subroutine...)
First of all, in Perl you are not required to define a subroutine before calling it; it would be a better practice to do so; hence the warning your code produces. However, there is nothing technically wrong in this regard; nor is this warning relevant to your problem.
I believe the answer is indeed in your two declarations of the same variable with "my", coupled with the specific behavior of the Perl interpreter. Here is the explanation of this warning from perldiag:
``my'' variable %s masks earlier declaration in same scope (S) A
lexical variable has been redeclared in the same scope, effectively
eliminating all access to the previous instance. This is almost always
a typographical error. Note that the earlier variable will still exist
until the end of the scope or until all closure referents to it are
destroyed.
When your print statement happens, only the first declaration of $P has been processed by the interpreter, thus it prints 10, as you would expect.
However, when you call the sub, Perl goes to look for the subroutine definition. It also has to find all of the other variable declarations preceding it, so that the sub can have access to lexical variables; it finds the second declaration, and thus your first $P is overwritten with a new $P. However, since this new $P hasn't been set to anything yet in your program, it is undefined.
"my $P" declares the variable. You do that twice, which you also should get an error for. change "my $P = 12;" to "$P = 12;" and you'll get what you want.
I would recommend you to read up a bit on perl (look at "perldoc perl" and "perldoc perlintro", or http://www.perl.org/learn.html)
my $P = 12;
You already declared $P(my $P = 10;) above, and should not do this again, remove the my,
display(); call the subroutine like this: &display();

How to check if variable is declared in perl?

I am using use strict; in perl and with that I use the following statement.
unless(defined($x)){
print "Not defined";
}
Where $x is not declared anywhere. So I expect it to print "Not defined" but it returns an error
Global symbol "$x" requires explicit package name at *********** in line 15.
The strict pragma has three parts: strict references, strict variables, and strict subs. The one you're running into is
strict vars
This generates a compile-time error if you access a variable that wasn't declared via our or use vars, localized via my, or wasn't fully qualified. Because this is to avoid variable suicide problems and subtle dynamic scoping issues, a merely local variable isn't good enough.
Because it generates compile-time errors, your non-BEGIN code won't even have a chance to run. You can temporarily allow non-strict variables inside a block as in
{
no strict 'vars';
print "Not defined!\n" unless defined $x;
}
but note that Perl's defined operator tells you whether a value is defined, not whether a variable has been declared.
Tell us more about your application, and we can give you better advice about how to handle it.
You can't even refer to a variable unless it's declared. When you ask
defined( $x ) ?
the compiler is going to complain: I don't know this what you're asking about, how am I supposed to tell it is defined? It has no point of reference for that variable, since you've indicated you do not want variables auto-created by name.
If strict 'vars' was not on--which it is by default when you use strict--then it would create an entry in the package symbol table for 'x'.
Interestingly enough is that without strict 'refs' it is also easy to check if a variable is in the package symbol table.
defined( *{ __PACKAGE__ . '::x' }{SCALAR} )
Since there is no way to auto-create a lexical ("my variables"), there is also not a standard way to check to see if lexicals are declared. Lexical variables are stored in the "pad". But there is a module PadWalker that can help.
In order to check the current level, you could get a hash of the pad, and then check whether or not it exists in the current pad. You could also loop back up through the stack (the integer argument works something like caller) to find where the most recent x was.
my $h = peek_my (0);
exists $h->{x};
I think you are mixing 'defined' and 'declared' concepts.
Your are asking for 'How to check if variable is declared in perl' but then you are checking if a variable is defined. These are two different concepts.
In perl if you use 'use strict' you are automatically checking for any variable not declared (using my, local or our). Once you have a variable declared, you can test if it is defined (have a value assigned).
So in your test, you are missing a prior declaration before testing for defineness
use strict;
my $x; # you are missing this part
[...] | # code
# your test for define
print defined $x? "defined\n" : "not defined\n";
Please be aware the testing for only $x is incorrect for your purpose:
my ($x,$y, $z);
$w; # not declared (use strict will catch it and die)
$x = 0; # declared and defined BUT if you make a logic test like 'if ($x) {}' then it will be FALSE, so don't confuse testing for **'$x'** and testing for **'defined $x'**
$y = undef; # declared but not defined
$z = 1; # declared, defined, and logial test TRUE
Finally the answer of xenorraticide seems faulty to me: he suggest 'unless $x' that is not correct for testing if defined as I said before. He also suggest 'unless exists $x', that is wrong for testing scalars. 'exists' test is only for hashes keys (and deprecated for arrays).
Hope this helps.
#
print "Not defined" if !defined($x);
result will be
Not defined
#
use strict;
print "Not defined" if !defined($x);
will generate error as in your question.
Look to: http://perldoc.perl.org/strict.html, where described how you can import only required restrictions. (However use strict 'vars' is very good idea :) )
Normally this kind of code should not be required for a serious program, but still why not just for fun: (assuming use strict)
print "Not defined\n" unless eval 'ref(\$x)';
#!/usr/bin/perl -l
use strict;
# if string below commented out, prints 'lol' , if the string enabled, prints 'eeeeeeeee'
#my $lol = 'eeeeeeeeeee' ;
# no errors or warnings at any case, despite of 'strict'
our $lol = eval {$lol} || 'lol' ;
print $lol;
my solution is
#!/usr/bin/perl -l
use strict;
# if string below commented out, prints 'lol' , if the string enabled, prints 'eeeeeeeee'
#my $lol = 'eeeeeeeeeee' ;
# no errors or warnings at any case, despite of 'strict'
our $lol = eval {$lol} || 'lol' ;
print $lol;
You can use unless, like this:
use 'strict';
my $var = 'defined';
unless (defined($var)) {
print "not defined\n";
}
$var = undef;
unless (defined($var)) {
print "not defined\n";
}
The first print will not print anything, the second one will since $var has been made undef.