【问题标题】:What is the default scope of foreach loop in Perl?Perl 中 foreach 循环的默认范围是什么?
【发布时间】:2010-02-10 16:43:53
【问题描述】:

在 Perl 中,在 foreach 循环中使用 'my' 有什么效果吗?无论是否使用“我的”,索引变量似乎总是本地的。那么你可以在 foreach 循环中删除'my'并且在循环体中仍然拥有私有作用域吗?

可以看出,使用'for'循环有使用/不使用'my'的区别:

use strict; 
use warnings; 

my ($x, $y) = ('INIT', 'INIT'); 

my $temp = 0; 

for ($x = 1; $x < 10; $x++) {
 $temp = $x+1; 
}

print "This is x: $x\n";   # prints 'This is x: 10'. 

for (my $y = 1; $y < 10; $y++) {
 $temp = $y+1; 
}

print "This is y: $y\n";   # prints 'This is y: INIT'. 

但在foreach上似乎没有效果:

my ($i, $j) = ('INIT', 'INIT'); 

foreach $i (1..10){
    $temp = $i+1;
}

print "\nThis is i: $i\n";   # prints 'This is i: INIT'. 



foreach my $j (1..10){
    $temp = $j+1;
}

print "\nThis is j: $j\n";   # prints 'This is j: INIT'. 

【问题讨论】:

  • 我们在Learning Perl 中介绍了这一点,它是foreach 循环文档的第一段。 :)
  • 提示:当您在代码前面加上 use strict; use warnings; 时会发生什么情况?
  • @Ether -- 添加了 strict 以清理主要示例。重点仍然集中在 for 和 foreach 默认上下文之间的区别上。感谢大家的帮助,我现在明白 foreach 循环中的默认范围是明确的“本地”或动态范围,而不是词法“我的”范围或全局“包”范围。
  • 我的意思是,当您拥有use strict; use warnings; 时,您根本不能省略my
  • @Ether。我添加了使用警告;并且——只要声明了变量——看起来还可以(至少在我运行它时)。你不能不省略'my'并获得默认的'local'范围吗?

标签: perl foreach scope


【解决方案1】:

来自http://perldoc.perl.org/perlsyn.html#Foreach-Loops

foreach 循环遍历一个普通的列表值,并依次将变量 VAR 设置为列表的每个元素。如果变量以关键字 my 开头,则它是词法范围的,因此仅在循环中可见。否则,该变量隐含地是循环的局部变量,并在退出循环时恢复其先前的值。如果变量之前用 my 声明过,它会使用那个变量而不是全局变量,但它仍然被本地化到循环中。这种隐式本地化只发生在 foreach 循环中。

【讨论】:

  • 来自原始海报。只是想添加一个综合来帮助像我这样的 perl 新手。如果有任何问题,请告诉我,我会清理它。上面的术语“隐式本地化/本地化”不仅仅是定性术语:实际上这里的“本地化”是一种精确的技术 Perl 语言结构。 'foreach' 使用动态的 'local' 范围,而不是词汇的 'my' 范围或全局的 'package' 范围。一个很好的链接,帮助我使用这 3 个范围 perl.plover.com/FAQs/Namespaces.html。之前,我很困惑——尽管 perl 文档中的“本地”与词法“我的”相同。
【解决方案2】:

Hans 链接到描述foreach 循环中的变量如何作用域的文档。区别很微妙,但可能很重要:

sub f { return $i }
$i = 4;
$m = $n = 0;

foreach    $i (1 .. 10) { $m += f() }
foreach my $i (1 .. 10) { $n += f() }

print "Result with localization:    $m\n";    #  ==>  55
print "Result with lexical scoping: $n\n";    #  ==>  40

【讨论】:

  • 你的帖子把我搞糊涂了。我预计$m 是 55,$n 是 40。直到我运行脚本,我才感到困惑。我认为您在 cmets 中有错误。
  • 抱歉@daotoad,cmets 中的错误。
【解决方案3】:

我对 Perl 最痛苦的发现之一是 foreach 循环中的“my”变量不是本地副本。当我发现我的阵列被破坏时,我感到很沮丧。

唯一的办法似乎是:


foreach ( @array ) {
  my $element = $_; # make a local copy
  ...
}

如果您查看perldoc,它会声明:“foreach 循环索引变量是您正在循环的列表中每个项目的隐式别名”和“如果 LIST 的任何元素是左值,您可以修改通过在循环内修改 VAR 来实现”。

【讨论】:

  • 你是对的。无论如何留下答案,因为这对我来说是一个很大的发现。
  • 如果你没想到它会很糟糕。但它也可以非常方便。例如,如果您需要对数组的所有成员应用转换:s/^\s+// for @foo; 同样适用于子参数,加上mapgrep。顺便说一句,List::MoreUtilsapply 类似于 map 但适用于本地副本,因此您可以在不修改 @foo 的情况下执行 my @bar = apply { s/Q/q/g } @foo;
  • 你这里提到的for的使用,其中$_指的是列表中的实际对象,很容易理解。然而,当说for my $var ( @array ) 时,新手很容易认为$var 是数组元素的本地副本。
【解决方案4】:

我刚刚对这个有趣的问题做了一些额外的研究。

foreach 不在乎你是否执行“my $y;”在循环之前。迭代器仍然只会在作用域内改变:

my $y;
foreach $y (1..10) {}

$y 将在 foreach 范围内发生变化,但当它离开时,它会恢复到循环之前的状态。

我曾想过也许 foreach 会自动执行“my $y;”在它开始运行之前,所以我尝试了严格模式,但那个解释并没有削减它,因为它仍然要求:

foreach my $y (1..10) {}

相对于:

foreach $y (1..10) {}

...如果“foreach $y”在功能上与“foreach my $y”相同,人们会认为这是不必要的。

【讨论】:

  • 不过,它们并不等同。 foreach my $y (1..10) {}; print $y;my $y; foreach $y (1..10); print $y; #same behavior with explicit 'my' in foreach loop
【解决方案5】:

foreach 循环中的迭代器实际上是循环本地的。也就是说,它确实直接保存列表中的值,实际上是按引用调用而不是按值调用。因此,如果您在循环中对值进行操作,它将更改列表中的值。但是,迭代器变量仅在引用循环时定义:

@names = qw(Kirk Spock Bones);

$officer = "Riker";
print "@names $officer\n"; # will print Kirk Spock Bones Riker

foreach $officer (@names) {
     $officer .= "y";
}

print "@names $officer\n"; # will print Kirky Spocky Bonesy Riker

因此,迭代器(在本例中为 $officer)实际上前面有一个“my”,以保留它在 foreach 循环之前具有的任何值。但是,如果它保存的值发生更改,则该更改将传递回您正在迭代的列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-29
    • 2013-10-01
    • 1970-01-01
    • 2011-01-21
    • 2010-10-17
    • 2017-04-10
    • 2014-03-28
    相关资源
    最近更新 更多