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 的本地子程式有根本的不同嗎?
uj5u.com熱心網友回復:
問題中的術語“本地子程式”似乎指的是詞法子程式。這些是私有子例程,僅在定義后的范圍(塊)內可見;就像私有變數一樣。
my但是它們是用or定義(或預先宣告)的state,如my sub subname { ... }
僅僅sub subname { ... }在另一個內部撰寫并不會使其成為“本地”(在任何版本的 Perl 中),但它的編譯就像它與其他子例程一起撰寫并放置在其包的符號表中(main::例如)。
問題在標題中提到了關閉,這是對此的評論
Perl中的閉包是程式中的結構,通常是標量變數,具有對子的參考,并且在(運行時)創建時從其作用域中攜帶環境(變數)。另見關于它的perlfaq7 條目。亂解釋。例如:
sub gen {
my $args = "@_";
my $cr = sub { say "Closed over: $args, my args: @_" }
return $cr;
}
my $f = gen( qw(args for gen) );
$f->("hi closed");
# Prints:
# Closed over: args for gen, my args: hi closed
匿名子“關閉”定義它的范圍內的變數,從某種意義上說,當它的生成函式回傳其參考并超出范圍時,由于該參考的存在,這些變數仍然存在。由于匿名 subs 是在運行時創建的,因此每次呼叫它的生成函式并重新制作其中的詞法時,anon sub 也會重新生成,因此它始終可以訪問當前值。因此,回傳的對 anon-sub 的參考使用了詞法資料,否則這些資料將消失。一個小魔法。?
回到“本地”潛艇的問題。如果我們想在問題中引入實際的閉包,我們需要從outer子例程回傳一個代碼參考,比如
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;
}
在這兩種情況下,都回傳了正確使用本地詞法變數 ( ) 及其最新值my $f = outer(...);的代碼參考。outer$x
但是我們不能在內部使用一個普通的名為 subouter的閉包
sub outer {
...
sub inner { ... } # misleading, likely misguided and buggy
return \&inner; # won't work correctly
}
因為這inner是在編譯時進行的,并且它使用的任何變數outer都將在第一次使用時烘焙它們的值,所以只有在第一次使用時才會正確。作為一個例子,我可以很容易地找到這篇文章,并查看perldiag 中的條目(或添加use diagnostics;到程式中)。
?在我看來,在某種程度上,一個窮人的物件,因為它具有功能和資料,在其他時間在其他地方制作,并且可以與傳遞給它的資料一起使用(并且兩者都可以更新)
uj5u.com熱心網友回復:
如果您想要“本地”潛艇,您可以根據您想要的向后兼容性級別使用以下之一:
5.26 :
my sub inner { ... }5.18 :
use experimental qw( lexical_subs ); # Safe: Accepted in 5.26. my sub inner { ... }“任何”版本:
local *inner = sub { ... };
但是,您不應該使用sub inner { ... }.
sub f { ... }
基本上是一樣的
BEGIN { *f = sub { ... } }
所以
sub outer {
...
sub inner { ... }
...
}
基本上是
BEGIN {
*outer = sub {
...
BEGIN {
*inner = sub { ... };
}
...
};
}
如您所見,inner即使在 之外也是可見的outer,因此它根本不是“本地的”。
正如您所看到的,分配到*inner是在編譯時完成的,這引入了另一個主要問題。
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
uj5u.com熱心網友回復:
我找到了一個相當好的解釋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');;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/493833.html
