对于Loop和Lexically Scoped变量

时间:2010-12-13 19:48:11

标签: perl

版本#1

use warnings;
use strict;

my $count = 4;

for $count (1..8) {
    print "Count = $count\n";
    last if ($count == 6);
}

if (not defined($count)) {
    print "Count not defined\n";
}
else {
    print "Count = $count\n";
}

打印:

1
2
3
4
5
6
4

为什么呢?因为for循环在其块内创建了自己的词法范围的$count版本。

版本#2

use warnings;
use strict;

my $count;
for $count (1..8) {
    print "Count = $count\n";
    last if ($count == 6);
}

if (not defined($count)) {
    print "Count not defined\n";
}
else {
    print "Count = $count\n";
}

1
2
3
4
5
6
Count not defined

糟糕!我想捕获$count的退出值,但for循环拥有它自己的$count的词法范围版本!我只是花了两个小时试图追查这个错误。

版本#3

use warnings;
use strict;

for $count (1..8) {
    print "Count = $count\n";
    last if ($count == 6);
}

print "That's all folks!\n";

这给了我错误Global symbol "$count" requires explicit package name at line 5.但是,我认为$countfor块内自动有词法范围。似乎只有当我已经在其他地方声明了这个变量的词法范围版本时才会发生这种情况。

这种行为的原因是什么?是的,我知道Conway的命令是你应该总是使用my作为for循环变量,但问题是为什么Perl解释器是这样设计的。

3 个答案:

答案 0 :(得分:8)

在Perl中,循环中对变量的赋值始终本地化为循环,循环变量始终是循环值的别名(意味着您可以通过修改循环变量来更改原始元素)。包变量(our)和词汇变量(my)都是如此。

这种行为最接近Perl动态范围包变量(使用local关键字),但也特别适用于词法变量(在循环中或在手之前声明)。

在任何情况下,循环结束后循环变量中的循环值都不会存在。对于循环范围变量,这是相当直观的,但对于范围超出循环的变量,行为类似于由循环创建的块范围内的本地化值(带local)。

for our $val (1 .. 10) {...} 

相当于:

our $val;
my @list = 1 .. 10;
my $i = 0;

while ($i < @list) {
   local *val = \$list[$i++];
   # loop body
}

在纯perl中,无法编写扩展的词法版本,但如果使用Data::Alias之类的模块:

my $val;
my @list = 1 .. 10;
my $i = 0;

while ($i < @list) {
   alias $val = $list[$i++];
   # loop body
}

答案 1 :(得分:5)

实际上,在版本#3 the variable is "localized" as opposed to lexically scoped中。

  

“foreach”循环遍历正常列表值并设置   变量VAR依次是列表的每个元素。如果变量   之前是关键字“my”,然后它是词法范围的,并且是   因此仅在循环内可见。 否则,变量是   隐含地循环本地并在退出时重新获得其原值   循环。 如果变量先前使用“my”声明,则使用   该变量而不是全局变量,但它仍然是本地化的   循环。这种隐式定位仅在“foreach”循环中发生。

在任何情况下,您都无法从for的那个stlye访问循环变量 - 循环外循环。但你可以使用另一种风格(C风格)for - 循环:

my $count;
for ($count=1; $count <= 8; $count++) {
    last if $count == 6;
}
...   # $count is now 6.

答案 2 :(得分:4)

  

为什么呢?因为for循环在其块内创建了自己的词法范围$count版本。

这是错误的。如果你写过for my $count (...) { ... }那就是真的,但你没有。相反,如果$count已经是全局的,那么本地化 - 已经存在的全局在执行循环期间设置为新值,并在完成时设置回来。应该从中明确区别:

our $x = "orig";

sub foo {
    print $x, "\n";
}

foo();

for $x (1 .. 3) {
  foo();
}

for my $x (1 .. 3) {
  foo();
}

输出

orig
1
2
3
orig
orig
orig

第一个for循环,没有my,正在更改已存在的全局$x的值。带有for的第二个my循环正在创建一个在循环外部不可见的新词法$x。他们不一样。

这也是示例#3失败的原因 - 因为范围中没有词法$count而你没有声明你打算触及全局包$count,{{1阻止你在你的轨道。对strict 'vars'循环而言,它的表现并没有任何不同。