69

我看到它们都用在我试图调试的这个脚本中,而文献只是不清楚。有人可以为我揭开这个神秘面纱吗?

4

15 回答 15

61

简短的回答是my在词法范围内local将变量标记为私有,在动态范围内将变量标记为私有。

更容易理解my,因为这会创建通常意义上的局部变量。创建了一个新变量,它只能在封闭的词法块内访问,通常用花括号标记。花括号规则有一些例外,例如:

foreach my $x (@foo) { print "$x\n"; }

但这只是 Perl 做你的意思。通常你有这样的事情:

sub Foo {
   my $x = shift;

   print "$x\n";
}

在这种情况下,$x它是子例程私有的,并且它的范围由花括号括起来。需要注意的是,与 相比local,变量的范围my是根据您在文件中写入的代码定义的。这是一种编译时现象。

要理解local,您需要考虑程序运行时的调用堆栈。当一个变量是local时,它会从该语句对堆栈上低于该语句的所有内容执行的点重新定义local,直到您将堆栈返回到包含local.

起初这可能会令人困惑,因此请考虑以下示例。

sub foo { print "$x\n"; }
sub bar { local $x; $x = 2; foo(); }

$x = 1;
foo(); # prints '1'
bar(); # prints '2' because $x was localed in bar
foo(); # prints '1' again because local from foo is no longer in effect

foo第一次被调用时,它看到的全局值为$x1。当bar被调用并local $x运行时,它重新定义$x了堆栈上的全局。现在当foo从 调用时bar,它会看到 2 的新值$x。到目前为止,这并不是很特别,因为如果没有调用 to 也会发生同样的事情local。神奇的是,当bar返回时,我们退出由创建的动态作用域,local $x而前一个全局$x变量又回到作用域。所以对于 的最终调用foo$x是 1。

您几乎总是想使用my,因为它为您提供了您正在寻找的局部变量。千载难逢local,做酷事真是得心应手。

于 2008-09-24T20:26:41.323 回答
43

动态范围。这是一个简洁的概念。许多人不使用它,也不理解它。

基本上可以认为my是创建一个变量并将其锚定到一个 {} 块,AKA 范围。

my $foo if (true); # $foo lives and dies within the if statement.

所以my变量是你习惯的。而使用动态作用域 $var 可以在任何地方声明并在任何地方使用。因此,local您基本上暂停使用该全局变量,并使用“局部值”来处理它。因此local为临时变量创建了一个临时范围。

$var = 4;
print $var, "\n";
&hello;
print $var, "\n";

# subroutines
sub hello {
     local $var = 10;
     print $var, "\n";
     &gogo; # calling subroutine gogo
     print $var, "\n";
}
sub gogo {
     $var ++;
}

这应该打印:

4
10
11
4
于 2008-09-24T20:24:08.863 回答
26

引用学习 Perl

但是 local 的名字是错误的,或者至少是误导性的。我们的朋友 Chip Salzenberg 说,如果他有机会乘坐时光机回到 1986 年并给拉里一条建议,他会告诉拉里用“save”这个名字来称呼本地人。 [14] 那是因为 local 实际上会将给定的全局变量的值保存起来,所以它稍后会自动恢复到全局变量。(没错:这些所谓的“局部”变量实际上是全局变量!)这种保存和恢复机制与我们现在已经见过两次的机制相同,分别在 foreach 循环的控制变量和 @_子程序参数数组。

因此,local保存全局变量的当前值,然后将其设置为某种形式的空值。您经常会看到它用于吞食整个文件,而不是仅引导一行:

my $file_content;
{
    local $/;
    open IN, "foo.txt";
    $file_content = <IN>;
} 

调用local $/将输入记录分隔符(Perl 停止读取“行”处的值)设置为空值,导致宇宙飞船操作员读取整个文件,因此它永远不会碰到输入记录分隔符。

于 2008-09-24T22:58:10.507 回答
18

我不敢相信没有人与 Mark Jason Dominus 关于此事的详尽论文有联系:

于 2008-09-25T23:25:27.733 回答
9

http://perldoc.perl.org/perlsub.html#Private-Variables-via-my()

与本地运算符创建的动态变量不同,使用 my 声明的词法变量对外界完全隐藏,包括任何调用的子程序。如果它是从自身或其他地方调用的同一个子例程,则这是正确的——每个调用都有自己的副本。

http://perldoc.perl.org/perlsub.html#Temporary-Values-via-local()

local 将其列出的变量修改为对封闭块、eval 或 do FILE 以及从该块中调用的任何子例程的“本地”。本地只是为全局(意味着包)变量提供临时值。它不会创建局部变量。这称为动态范围。词法作用域是用 my 完成的,它更像 C 的自动声明。

我认为这一点都不清楚,除了说“本地到封闭块”之外,它的意思是在退出块时恢复原始值。

于 2008-09-24T20:21:35.623 回答
7

好吧,谷歌真的很适合你:http ://www.perlmonks.org/?node_id=94007

从链接:

快速总结:'my' 创建一个新变量,'local' 临时修改变量的值。

即,“本地”临时更改变量的值,但仅 在它存在的范围内。

通常使用我的,它更快并且不会做任何奇怪的事情。

于 2008-09-24T20:14:02.117 回答
6

来自man perlsub

与本地运算符创建的动态变量不同,使用 my 声明的词法变量对外界完全隐藏,包括任何调用的子程序。

因此,过度简化my会使您的变量仅在声明的地方可见。local使其在调用堆栈中也可见。您通常会想要使用my而不是local.

于 2008-09-24T20:18:10.017 回答
4

你的困惑是可以理解的。词法作用域相当容易理解,但动态作用域是一个不寻常的概念。名称使情况变得更糟,my并且local由于历史原因有些不准确(或至少不直观)。

my声明一个词法变量——从声明点到封闭块(或文件)结束都是可见的。它完全独立于程序其余部分中具有相同名称的任何其他变量。它是该块私有的。

local另一方面,声明对全局变量值的临时更改。更改在封闭范围的末尾结束,但变量——是全局的——在程序的任何地方都是可见的。

根据经验,使用my来声明您自己的变量并local控制更改 Perl 内置变量的影响。

如需更详尽的描述,请参阅 Mark Jason Dominus 的文章Coping with Scopeping

于 2008-09-25T02:00:13.330 回答
3

local 是一种较老的本地化方法,从 Perl 只有动态作用域的时代开始。词法作用域对程序员来说更自然,在许多情况下也更安全。我的变量属于声明它们的范围(块、包或文件)。

相反,局部变量实际上属于全局命名空间。如果你用 local 来引用变量 $x,你实际上是在引用 $main::x,它是一个全局变量。与它的名字所暗示的相反,所有 local 所做的只是将一个新值推送到 $main::x 的值堆栈中,直到该块结束,此时旧值将被恢复。这本身就是一个有用的功能,但由于多种原因,它不是一个拥有局部变量的好方法(想想当你有线程时会发生什么!想想当你调用一个真正想要使用全局变量的例程时会发生什么?你已经本地化了!)。然而,在 Perl 5 之前的糟糕日子里,这是让变量看起来像局部变量的唯一方法。我们仍然坚持使用它。

于 2008-09-25T18:52:22.490 回答
2

“我的”变量仅在当前代码块中可见。“本地”变量在以前可见的地方也可见。例如,如果你说“我的 $x;” 并调用一个子函数,它看不到那个变量 $x。但如果你说“本地 $/;” (以清除记录分隔符的值)然后您更改从文件读取的方式在您调用的任何函数中工作。

在实践中,您几乎总是想要“我的”,而不是“本地的”。

于 2008-09-24T20:18:36.240 回答
2

查看以下代码及其输出以了解差异。

our $name = "Abhishek";

sub sub1
{
    print "\nName = $name\n";
    local $name = "Abhijeet";

    &sub2;
    &sub3;
}

sub sub2
{
    print "\nName = $name\n";
}

sub sub3
{
    my $name = "Abhinav";
    print "\nName = $name\n";
}


&sub1;

输出是:

Name = Abhishek

Name = Abhijeet

Name = Abhinav
于 2013-04-10T05:44:22.113 回答
0

dinomite 使用 local 重新定义记录分隔符的示例是我在许多 perl 编程中遇到的唯一一次。我生活在一个利基 perl 环境 [安全编程] 中,但根据我的经验,它确实是一个很少使用的范围。

于 2008-10-01T16:01:16.710 回答
0
&s;

sub s()
{
    local $s="5";
    &b;
    print $s;
}

sub b()
{
    $s++;
}

上面的脚本打印 6。

但是,如果我们将 local 更改为 my ,它将打印 5。

这就是区别。简单的。

于 2009-08-06T08:12:41.570 回答
0

我认为最容易记住它的方法就是这样。MY 创建一个新变量。LOCAL 临时更改现有变量的值。

于 2014-09-17T14:47:37.797 回答
0

仅当您在子例程中调用了子例程时,它才会有所不同,例如:

sub foo { 
    print "$x\n"; 
}
sub bar { my $x; $x = 2; foo(); }
    
bar(); 

它不打印任何内容,因为$x受到{}bar 的限制,并且对调用的子例程不可见,例如:

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

sub bar { local $x; $x = 2; foo(); }
   
bar(); 

它将打印 2,因为局部变量对被调用的子例程可见。

于 2021-08-14T11:50:39.917 回答