【问题标题】:Shared variables in the context of subroutines vs anonymous subroutines子例程与匿名子例程上下文中的共享变量
【发布时间】:2013-05-07 17:32:56
【问题描述】:

我在另一个帖子的回答中看到了这段代码:Why would I use Perl anonymous subroutines instead of a named one?,但无法弄清楚到底发生了什么,所以我想自己运行它。

sub outer
{
  my $a = 123;

  sub inner
  {
    print $a, "\n"; #line 15 (for your reference, all other comments are the OP's)
  }

  # At this point, $a is 123, so this call should always print 123, right?
  inner();

  $a = 456;
}

outer(); # prints 123
outer(); # prints 456! Surprise!

在上面的示例中,我收到一条警告:“变量 $a 将不会在第 15 行保持共享。 显然,这就是输出“意外”的原因,但我仍然不明白这里发生了什么。

sub outer2
{
  my $a = 123;

  my $inner = sub
  {
    print $a, "\n";
  };

  # At this point, $a is 123, and since the anonymous subrotine 
  # whose reference is stored in $inner closes over $a in the 
  # "expected" way...
  $inner->();

  $a = 456;
}

# ...we see the "expected" results
outer2(); # prints 123
outer2(); # prints 123

同样,我也不明白这个例子中发生了什么。有人可以解释一下吗?

提前致谢。

【问题讨论】:

    标签: perl


    【解决方案1】:

    它与子例程的编译时与运行时解析有关。正如diagnostics 消息所说,

    调用内部子程序时,会看到 first 之前和期间的外部子例程变量 调用外部子程序;在这种情况下,在第一次调用 外部子程序完成后,内部子程序和外部子程序将没有 不再共享变量的公共值。换句话说, 变量将不再共享。

    注释您的代码:

    sub outer
    {
      # 'my' will reallocate memory for the scalar variable $a
      # every time the 'outer' function is called. That is, the address of
      # '$a' will be different in the second call to 'outer' than the first call.
    
      my $a = 123;
    
    
      # the construction 'sub NAME BLOCK' defines a subroutine once,
      # at compile-time.
    
      sub inner1
      {
    
        # since this subroutine is only getting compiled once, the '$a' below
        # refers to the '$a' that is allocated the first time 'outer' is called
    
        print "inner1: ",$a, "\t", \$a, "\n"; 
      }
    
      # the construction  sub BLOCK  defines an anonymous subroutine, at run time
      # '$inner2' is redefined in every call to 'outer'
    
      my $inner2 = sub {
    
        # this '$a' now refers to '$a' from the current call to outer
    
        print "inner2: ", $a, "\t", \$a, "\n";
      };
    
      # At this point, $a is 123, so this call should always print 123, right?
      inner1();
      $inner2->();
    
      # if this is the first call to 'outer', the definition of 'inner1' still
      # holds a reference to this instance of the variable '$a', and this
      # variable's memory will not be freed when the subroutine ends.
    
      $a = 456;
    }
    outer();
    outer();
    

    典型输出:

    inner1: 123     SCALAR(0x80071f50)
    inner2: 123     SCALAR(0x80071f50)
    inner1: 456     SCALAR(0x80071f50)
    inner2: 123     SCALAR(0x8002bcc8)
    

    【讨论】:

    • “解析”在这里可能是错误的词,但“编译”似乎也有点错误:IIRC,对于闭包,编译的代码只是组合到一个新的环境/范围,从而产生一个新的 CV,而命名的 subs 永远不会反弹到新的范围(没有重新定义)。
    • 非常感谢,这很有帮助!
    【解决方案2】:

    您可以打印 \&inner;在第一个示例中(定义后),并打印 $inner;秒。

    您看到的是十六进制代码引用,在第一个示例中相等,而在第二个示例中不同。 所以,在第一个例子中,inner 只被创建了一次,并且它总是在第一次调用 outer() 时闭包到 $a 词法变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-24
      • 2015-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多