封闭中的本地变量可见性与本地`

发布于 2025-02-08 16:08:21 字数 652 浏览 3 评论 0 原文

Perl 5.18.2似乎接受“本地子例程”。

示例:

sub outer()
{
    my $x = 'x';   # just to make a simple example

    sub inner($)
    {
        print "${x}$_[0]\n";
    }

    inner('foo');
}

如果没有“本地子例程”,我会写的:

#...
    my $inner = sub ($) {
        print "${x}$_[0]\n";
    }

    $inner->('foo');
#...

最重要的是,我认为两者都是等效的。

但是,第一个变体无法正常运行,因为Perl抱怨:

变量$ x在...

上不可用

... 中描述该行 $ x 在“本地子例程”中引用的线路。

谁能解释这一点? Perl的本地子例程与Pascal的本地子例程根本不同吗?

Perl 5.18.2 accepts "local subroutines", it seems.

Example:

sub outer()
{
    my $x = 'x';   # just to make a simple example

    sub inner($)
    {
        print "${x}$_[0]\n";
    }

    inner('foo');
}

Without "local subroutines" I would have written:

#...
    my $inner = sub ($) {
        print "${x}$_[0]\n";
    }

    $inner->('foo');
#...

And most importantly I would consider both to be equivalent.

However the first variant does not work as Perl complains:

Variable $x is not available at ...

where ... describes the line there $x is referenced in the "local subroutine".

Who can explain this; are Perl's local subroutines fundamentally different from Pascal's local subroutines?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

江湖彼岸 2025-02-15 16:08:21

问题中的术语“ local subroutine ”似乎是指。这些是私人子例程,仅在定义之后定义的范围(块)内可见;就像私人变量一样。

state 定义(或预定的)

但是它们是用我的 > sub subname {...} 在另一个内部的内部没有使其“本地”(在任何版本的perl中),但是将其编译成与其他子例程一起编写的,并将其放置在他们的软件包的符号表( main :: 例如)。


问题提到 cloture> cloture 在标题中,这里是评论

a perl中的关闭是程序中的结构,通常是标量变量,引用子,并在其(运行时)创建其范围中带有环境(变量)。另请参阅a perlfaq7条目。凌乱解释。例如:

sub gen { 
    my $args = "@_"; 

    my $cr = sub { say "Closed over: |$args|. Args for this sub: @_" }
    return $cr;
}

my $f = gen( qw(args for gen) );

$f->("hi closed");
# Prints:
# Closed over: |args for gen|. Args for this sub: hi closed

匿名子“关闭”范围中定义的变量,从某种意义上说,当它的生成函数返回其引用并删除范围并远离这些变量,这些变量仍然存在,因为该引用的存在。
由于在运行时创建了匿名subs,因此每次调用其生成函数时,它都会重新制作词汇,因此Anon Sub也是如此,因此它始终可以访问当前值。
因此,返回的对Anon-Sub的引用使用词汇数据,否则这些数据将消失。一小部分魔术。

回到“本地”潜艇的问题。如果我们想引入实际关闭问题,我们需要从 offer subroutine返回代码参考,例如

sub outer {
    my $x = 'x' . "@_";
    return sub { say "$x @_" }
}
my $f = outer("args");
$f->( qw(code ref) );   # prints:  xargs code ref

或根据主要问题,如 v5.18.0 v5.26.0 ,我们可以使用一个命名的词汇(真正嵌套了(真正嵌套) !)

sub outer {
    my $x = 'x' . "@_";
    
    my sub inner { say "$x @_" };

    return \&inner;
}

在这两种情况下,子例程我的$ f = ofter(...); 的代码参考从 ofert 返回,该参考正确使用了本地词汇变量( $ $ X ),其最新值。

但是我们不能使用外部闭合的平原sub in

sub outer {
    ...

    sub inner { ... }  # misleading, likely misguided and buggy

    return \&inner;    # won't work correctly
}

在编译时进行是全局的,因此它使用 ofter 从 ofter 首次称为时,将其值烘烤。因此, Inner 只有在 exter 下次称为 ofter 中的词汇环境时,才能正确才是正确的/代码>不。例如,我可以很容易地找到这篇文章,然后参见条目


,在我看来,在某种程度上,一个贫穷的对象,因为它具有功能性和数据,在另一个时间在其他地方制作,并且可以与传递给它的数据一起使用(并且都可以更新)

The term "local subroutine" in the question seems to be referring to lexical subroutines. These are private subroutines visible only within the scope (block) where they are defined, after the definition; just like private variables.

But they are defined (or pre-declared) with my or state, as my sub subname { ... }

Just writing a sub subname { ... } inside of another doesn't make it "local" (in any version of Perl), but it is compiled just as if it were written alongside that other subroutine and is placed in their package's symbol table (main:: for example).


The question mentions closure in the title and here is a comment on that

A closure in Perl is a structure in a program, normally a scalar variable, with a reference to a sub and which carries environment (variables) from its scope at its (runtime) creation. See also a perlfaq7 entry on it. Messy to explain. For example:

sub gen { 
    my $args = "@_"; 

    my $cr = sub { say "Closed over: |$args|. Args for this sub: @_" }
    return $cr;
}

my $f = gen( qw(args for gen) );

$f->("hi closed");
# Prints:
# Closed over: |args for gen|. Args for this sub: hi closed

The anonymous sub "closes over" the variables in scope where it's defined, in a sense that when its generating function returns its reference and goes out of scope those variables still live on, because of the existence of that reference.
Since anonymous subs are created at runtime, every time its generating function is called and lexicals in it remade so is the anon sub, so it always has access to current values.
Thus the returned reference to the anon-sub uses lexical data, which would otherwise be gone. A little piece of magic.

Back to the question of "local" subs. If we want to introduce actual closures to the question, we'd need to return a code reference from the outer subroutine, like

sub outer {
    my $x = 'x' . "@_";
    return sub { say "$x @_" }
}
my $f = outer("args");
$f->( qw(code ref) );   # prints:  xargs code ref

Or, per the main question, as introduced in v5.18.0 and stable from v5.26.0, we can use a named lexical (truly nested!) subroutine

sub outer {
    my $x = 'x' . "@_";
    
    my sub inner { say "$x @_" };

    return \&inner;
}

In both cases my $f = outer(...); has the code reference returned from outer which correctly uses the local lexical variables ($x), with their most current values.

But we cannot use a plain named sub inside outer for a closure

sub outer {
    ...

    sub inner { ... }  # misleading, likely misguided and buggy

    return \&inner;    # won't work correctly
}

This inner is made at compile time and is global so any variables it uses from outer will have their values baked from when outer was called the first time. So inner will be correct only until outer is called the next time -- when the lexical environment in outer gets remade but inner doesn't. As an example I can readily find this post, and see the entry in perldiag (or add use diagnostics; to the program).


And in my view a poor-man's object in a way, as it has functionality and data, made elsewhere at another time and which can be used with data passed to it (and both can be updated)

少女七分熟 2025-02-15 16:08:21

如果您需要“本地”潜艇,则可以根据所需的向后兼容性级别使用以下内容:

  • 5.26+:

     我的子内{...}
     
  • 5.18+:

     使用实验QW(lexical_subs); #安全:在5.26中接受。
    
    我的子内部{...}
     
  • “任何”版本:

      local *inner = sub {...};
     

但是,您不应该使用 sub Inner {...}


sub f { ... }

相同

BEGIN { *f = sub { ... } }

基本上与

sub outer {
   ...

   sub inner { ... }

   ...
}

您基本上

BEGIN {
   *outer = sub {
      ...

      BEGIN {
         *inner = sub { ... };
      }

      ...
   };
}

,即使在 外部之外也可以看到内部,因此它根本不是“本地”。

如您所见,对*内部的分配是在编译时完成的,这引入了另一个主要问题。

use strict;
use warnings;
use feature qw( say );

sub outer {
   my $arg = shift;

   sub inner {
      say $arg;
   }

   inner();
}

outer( 123 );
outer( 456 );
Variable "$arg" will not stay shared at a.pl line 9.
123
123

5.18确实引入了词汇(“本地”)子例程。

use strict;
use warnings;
use feature qw( say );
use experimental qw( lexical_subs );  # Safe: Accepted in 5.26.

sub outer {
   my $arg = shift;

   my sub inner {
      say $arg;
   };

   inner();
}

outer( 123 );
outer( 456 );
123
456

如果您需要支持旧版本的Perl,则可以使用以下内容:

use strict;
use warnings;
use feature qw( say );

sub outer {
   my $arg = shift;

   local *inner = sub {
      say $arg;
   };

   inner();
}

outer( 123 );
outer( 456 );
123
456

If you want "local" subs, you can use one of the following based on the level of backward compatibility you want:

  • 5.26+:

    my sub inner { ... }
    
  • 5.18+:

    use experimental qw( lexical_subs );  # Safe: Accepted in 5.26.
    
    my sub inner { ... }
    
  • "Any" version:

    local *inner = sub { ... };
    

However, you should not, use sub inner { ... }.


sub f { ... }

is basically the same as

BEGIN { *f = sub { ... } }

so

sub outer {
   ...

   sub inner { ... }

   ...
}

is basically

BEGIN {
   *outer = sub {
      ...

      BEGIN {
         *inner = sub { ... };
      }

      ...
   };
}

As you can see, inner is visible even outside of outer, so it's not "local" at all.

And as you can see, the assignment to *inner is done at compile-time, which introduces another major problem.

use strict;
use warnings;
use feature qw( say );

sub outer {
   my $arg = shift;

   sub inner {
      say $arg;
   }

   inner();
}

outer( 123 );
outer( 456 );
Variable "$arg" will not stay shared at a.pl line 9.
123
123

5.18 did introduce lexical ("local") subroutines.

use strict;
use warnings;
use feature qw( say );
use experimental qw( lexical_subs );  # Safe: Accepted in 5.26.

sub outer {
   my $arg = shift;

   my sub inner {
      say $arg;
   };

   inner();
}

outer( 123 );
outer( 456 );
123
456

If you need to support older versions of Perl, you can use the following:

use strict;
use warnings;
use feature qw( say );

sub outer {
   my $arg = shift;

   local *inner = sub {
      say $arg;
   };

   inner();
}

outer( 123 );
outer( 456 );
123
456
白衬杉格子梦 2025-02-15 16:08:21

我从 Man Perldiag 中找到了一个很好的解释:

       Variable "%s" is not available
           (W closure) During compilation, an inner named subroutine or eval
           is attempting to capture an outer lexical that is not currently
           available.  This can happen for one of two reasons.  First, the
           outer lexical may be declared in an outer anonymous subroutine
           that has not yet been created.  (Remember that named subs are
           created at compile time, while anonymous subs are created at run-
           time.)  For example,

               sub { my $a; sub f { $a } }

           At the time that f is created, it can't capture the current value
           of $a, since the anonymous subroutine hasn't been created yet.

因此,这是一个可能的解决方法:

sub outer()
{
    my $x = 'x';   # just to make a simple example

    eval 'sub inner($)
    {
        print "${x}$_[0]\n";
    }';

    inner('foo');;
}

...虽然不会:

sub outer()
{
    my $x = 'x';   # just to make a simple example

    eval {
        sub inner($)
        {
            print "${x}$_[0]\n";
        }
    };

    inner('foo');;
}

I found a rather good explanation from man perldiag:

       Variable "%s" is not available
           (W closure) During compilation, an inner named subroutine or eval
           is attempting to capture an outer lexical that is not currently
           available.  This can happen for one of two reasons.  First, the
           outer lexical may be declared in an outer anonymous subroutine
           that has not yet been created.  (Remember that named subs are
           created at compile time, while anonymous subs are created at run-
           time.)  For example,

               sub { my $a; sub f { $a } }

           At the time that f is created, it can't capture the current value
           of $a, since the anonymous subroutine hasn't been created yet.

So this would be a possible fix:

sub outer()
{
    my $x = 'x';   # just to make a simple example

    eval 'sub inner($)
    {
        print "${x}$_[0]\n";
    }';

    inner('foo');;
}

...while this one won't:

sub outer()
{
    my $x = 'x';   # just to make a simple example

    eval {
        sub inner($)
        {
            print "${x}$_[0]\n";
        }
    };

    inner('foo');;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文