NedStarkOfWinterfell
NedStarkOfWinterfell

Reputation: 5153

Not localizing loop variable gives different result

When I type in the following code and run it, it types <FONT COLOR='foo'></FONT>. However, when I add my to the loop variable (for my $name (@colors)), it types the expected <FONT COLOR='red'></FONT>. Can anyone explain why?

@colors = qw(red blue green yellow orange purple violet);
$name = 'foo';
for $name (@colors) {
  no strict 'refs';
  *$name = sub { "<FONT COLOR='$name'></FONT>" };
}
print red();

Upvotes: 2

Views: 92

Answers (3)

ikegami
ikegami

Reputation: 385897

Closures capture variables, not values.

Think of for my $x as creating a new variable every time through the loop. The inner sub captures this variable instead of the similarly named package variable whose value never changes from foo.

When you remove the my, only one variable is created (a package variable), so each sub created in the loop refes to the same variable (whose value goes from foo to red to blue to ... to violet to foo).

Upvotes: 2

tauli
tauli

Reputation: 1420

The variable used in a for loop has some magic attached to it. For each iteration of the loop, it is set to the appropriate value. After the end of the loop, $name is set to it's old value. Basically, every sub you create sees the same variable which changes it's value. I've modified your example to demonstrate this:

@colors = qw(red blue green yellow orange purple violet);
$name = 'foo';
for $name (@colors) {
  no strict 'refs';
  *$name = sub { "<FONT COLOR='$name'></FONT>" };
  print $name . &{$name} . "\n";
}
print red() . "\n";

You can create a local variable by defining it with the function my like this:

for my $name (@colors) {

As a rule of thumb, you should always use strict; in your programs, which enforces initialization of variables.

Upvotes: 1

amon
amon

Reputation: 57640

In your loop you create several subs. These subs can see all the variables of the context that they are created in.

If we use local / global variables, the sub will always see the latest value (The interpolation of the variable into the string does'nt happen at compile-time or "definition-time", but when the sub is executed.) In our case, the current value outside the loop is foo.

If we use lexical variables with my, the variable that we used inside the loop is invisible outside of the loop and invisible in all other iterations of the loop. However, it is still visible to the sub itself. This is called a closure. Closures only work with lexical variables and are a powerful method to achieve information hiding or to construct specifically tailored subs like in the example code.

Upvotes: 3

Related Questions