Perl范围函数奇怪吗?

时间:2015-09-17 13:16:42

标签: perl function scope

#!/usr/bin/env perl

use strict;
use warnings FATAL => qw ( all );

my ( $func, @list, $num );

$func = sub {
    print $num // "undef"; print "\n";
};

@list = ( 1,2,3 );

foreach $num ( @list ) {
    $func->();
};

这件perl打印

undef
undef 
undef 

而不是

1
2
3

$func例程可以看到@list,为什么不$num

4 个答案:

答案 0 :(得分:4)

因为foreach循环隐式地本地化它们的迭代器变量。

请参阅:perlsyn

  

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

但实际上 - 这并不经常出现,因为从外部做任何类型的循环迭代器都是非常糟糕的形式。缩小范围,并传递变量以避免错误和故障排除。

答案 1 :(得分:1)

您正在访问$num变量的本地化版本,如Sobrique所说。您的意图是使用对变量$num的引用。这就是我在这里展示的内容:

my ( $func, @list, $num );
$func = sub {
    print $$num // "undef"; print "\n";
};

@list = ( 1,2,3 );

foreach ( @list ) {
    $num = \$_;
    $func->();
};

您还可以使用变量的全局版本,而不是词汇:

my ( $func, @list, $num );

$func = sub {
    print $main::num // "undef"; print "\n";
};

@list = ( 1,2,3 );

foreach $main::num ( @list ) {
    $func->();
};

但这是一种愚蠢的方式来封装它。您不应该在子例程中使用全局变量。这不是好习惯。相反,将值传递给sub并通过@_变量(在本例中为第一个数组索引$_[0])访问它:

my $func = sub {
    print $_[0] // "undef"; print "\n";
};

my @list = ( 1,2,3 );

for ( @list ) {
    $func->($_);
};

我还修改了上面的一些惯用编码风格。

答案 2 :(得分:0)

嗯,从文档中猜出原因是这样的:

  

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

这意味着我不应该使用foreach循环,但是为了或者同时...

答案 3 :(得分:0)

我可以引用perlsyn,但perlsyn似乎并没有说清楚是什么情况。变量不是“本地化的”,并且在退出循环时没有任何东西“重新获得”它的值。声明循环变量时外部变量隐藏,并且在循环之后可见。

隐式隐藏外部作用域中的变量,保留其值。

此代码剪贴簿的输出将说明:

use 5.022;
use strict;
use warnings FATAL => qw ( all );
use Data::Dumper;

my ( $func, @list, $num );

$func = sub {
    print $num // "undef"; print "\n";
};
$num = 101;
$func->();

my $np = \$num;
print Data::Dumper->Dump( [ $np ], [ '*np' ] );

@list = ( 1,2,3 );

foreach $num ( @list ) {
    print Data::Dumper->Dump( [ $num ], [ '*num' ] );
    print Data::Dumper->Dump( [ $np ], [ '*np' ] );
    my $np2 = \$num;
    print Data::Dumper->Dump( [ $np2 ], [ '*np2' ] );
    print Data::Dumper->Dump( [ $np ], [ '*np' ] );
    $$np++;
    $func->();
}

哪个输出:

101
$np = \101;
$num = 1;
$np = \101;
$np2 = \1;
$np = \101;
102
$num = 2;
$np = \102;
$np2 = \2;
$np = \102;
103
$num = 3;
$np = \103;
$np2 = \3;
$np = \103;
104

因此,在循环内部,符号$num根本不会引用$np指向的内存,而是引用$np2指向的内存。