Re: foreach-loop-local var in sub
by choroba (Cardinal) on Jan 21, 2013 at 16:02 UTC
|
Defining a named sub in a loop is not doing what you think. The sub is defined just once, using the $i of the first iteration. On the next iteration, a new $i is created by my, but the sub is not redefined and still references the previous $i. It is better to use parameters to pass values to subroutines.If you remove my (and, under strict, replace it with our), the intended behaviour will occur, using a global variable.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Thank you. this answered my question. I didn't know about "our" (and still don't, but at least now I know what I should be looking for).
-warshall
| [reply] [Watch: Dir/Any] |
|
I didn't know about "our" (and still don't, but
at least now I know what I should be looking for)
Nooo!!! You should not be looking at our!
Lexical variables (my) are what you should
be learning about first and they should be preferred to global
variables (our) in almost all cases.
Given you are a Perl beginner, you need to master the basics
of scoping and how to write subroutines.
Start by defining all your subroutines at the top of the
file and pass parameters to each subroutine.
Do not succumb to the evil of having subroutines use global variables.
Instead think about your code, what each subroutine does and what it needs,
and pass parameters to each subroutine for use as local
variables within the subroutine.
For example:
use strict;
use warnings;
sub my_print
{
my $i = shift;
print $i;
}
foreach my $j (0, 1) {
my_print($j);
}
Further reading from the Perl monks Tutorials section:
As for learning Perl, take a look at learn.perl.org and Perl Tutorial Hub.
Also, be sure to refer to the
Perl online documentation.
Good luck!
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
what I should be looking for
I would recommend looking for subroutine parameters, rather. See perlsub for details.
| [reply] [Watch: Dir/Any] |
|
I think you are searching for anonymous subroutines and closures, but given that you indeed seem like a programming/Perl newbie, you shouldn't be venturing there and instead heed to the advice given in 1014512 above.
| [reply] [Watch: Dir/Any] |
Re: foreach-loop-local var in sub
by tobyink (Canon) on Jan 21, 2013 at 16:06 UTC
|
sub definitions within control structures don't really work how you want them to work. Your my_print function only gets defined once; not each time around the loop. If you want to redefine the sub each time around the loop, use an anonymous sub (a.k.a. closure; coderef) - which can be given a name by assigning it to a glob.
This works:
use strict;
use warnings;
no warnings 'redefine';
# stub to declare sub name, allowing it to be used
# as a bareword later on
sub my_print ();
foreach my $i (0, 1) {
*my_print = sub () {
print "[$i]\n";
};
my_print;
}
package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: foreach-loop-local var in sub
by blue_cowdawg (Monsignor) on Jan 21, 2013 at 15:55 UTC
|
First off you've declared the sub as having no arguments. Secondly within the curly braces of the sub you have changed context and $i is uninitialized within that context and therefor you are going to print zeros for each iteration.
Here is code that does what you think it should:
foreach (0, 1) {
my $i = $_;
sub my_print {
my $i = shift;
print $i;
}
my_print( $i);
}
Now... you could have saved yourself a lot of pain if you had
use strict;
use warnings;
at the beginning of your code. Bailiff, lock him up.
Peter L. Berghold -- Unix Professional
Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
you have changed context and $i is uninitialized within that context and therefor you are going to print zeros
Not exactly true. Try with
foreach (qw(a b)) {
Update: Do not use numbers for testing behaviour, see Re^3: variable declaration question.
| [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
|
Re: foreach-loop-local var in sub
by Athanasius (Archbishop) on Jan 21, 2013 at 16:33 UTC
|
Hello warshall, and welcome to the Monastery!
Just to reinforce the explanations given by choroba and tobyink, here is what the Camel Book (4th Edition, page 358) says about Nested subroutines:
If you are accustomed (from other programming languages) to using subroutines nested within other subroutines, each with their own private variables, you’ll have to work at it a bit in Perl. Named subroutines do not nest properly, although anonymous ones do.9
9 To be more precise, globally named subroutines don’t nest. Unfortunately, that’s the only kind of named subroutine declaration we have. We haven’t yet implemented lexically scoped, named subroutines (known as my subs), but when we do, they should nest correctly.
Hope that helps,
| [reply] [Watch: Dir/Any] [d/l] |
|
FYI, Perl 5.18 will have experimental implementations of my sub, state sub and our sub. our sub is effectively the same as the existing sub keyword but can also be used to hide my sub subs, a la:
use 5.010;
our $foo = 42;
my $foo = 99;
say $foo; # says 99
our $foo; # "hides" my $foo
say $foo; # says 42
package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
| [reply] [Watch: Dir/Any] [d/l] |
|
|
Re: foreach-loop-local var in sub
by LanX (Saint) on Jan 21, 2013 at 17:51 UTC
|
use strict;
use warnings;
foreach (0, 1) {
my $i = $_;
my $print = sub {
print $i;
};
$print->(); # 0,1
}
or just not expecting to be able to easily redefine package subs at runtime, they are defined only once at compile time:
use strict;
use warnings;
my $i;
sub my_print {
print $i;
};
foreach (0, 1) {
$i = $_;
my_print(); # 0,1
}
Anyway, while there are good use-cases for closures, I'd expect a beginner to go easy and pass arguments normally :
use strict;
use warnings;
sub my_print {
my ($i)=@_;
print $i;
};
foreach (0, 1) {
my_print($_); # 0,1
}
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
I get strange results from a slight variation:
use strict;
use warnings;
my $i = 6;
sub my_print {
print $i;
};
for ($i = 1; $i < 3; $i++) {
my_print();
print " (C-Style:\$i==$i)\n";
}
for $i (qw|x y|) {
my_print();
print " (Perl Style:\$i==$i)\n";
}
Output:
1 (C-Style:$i==1)
2 (C-Style:$i==2)
3 (Perl Style:$i==x)
3 (Perl Style:$i==y)
Not that I would write code like this, but how come the "perl style" does not update the "$i" the sub sees ?
Most people believe that if it ain't broke, don't fix it.
Engineers believe that if it ain't broke, it doesn't have enough features yet.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
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 its still localized to
the loop.
If you also check the references you will see that the '$i' are pointing to different locations.
use strict;
use warnings;
$,=",";
$\="\n";
my $i = 6;
sub my_print {
print $i,\$i;
}
;
for $i (qw|x y|) {
my_print();
print " (Perl Style:\$i==$i)",\$i,"\n";
}
OUTPUT
6, SCALAR(0x8fa4e38)
(Perl Style:$i==x), SCALAR(0x8f86760),
6, SCALAR(0x8fa4e38)
(Perl Style:$i==y), SCALAR(0x8fa4da8),
That's why PBP says to always use lexical loop vars in foreach!
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
|
Re: foreach-loop-local var in sub
by Anonymous Monk on Jan 21, 2013 at 16:41 UTC
|
This also reinforces the need to always specify use strict; use warnings; in everything you do. Perl won't generate error- or warning-messages when maybe you think it should. | [reply] [Watch: Dir/Any] |
|
In this particular case, the code runs without complaints even under strict and warnings.
| [reply] [Watch: Dir/Any] |