在JavaScript中使用自调用匿名函数来扩展变量等是一种常见做法:
;(function() {
...
})();
在Perl中使用这些函数是一个好习惯吗?
(sub {
...
})->();
或者出于某种原因使用main子程序更好吗?
sub main {
...
}
main();
答案 0 :(得分:6)
Perl有JS缺乏的词汇范围机制。你最好简单地将你想要的代码封装在一个块中,例如:
{
my $localvar;
. . .
}
在这种情况下,$localvar
在这些大括号之外将完全不可见;这也是可以用来本地化内置变量的机制,例如$/
:
{
local $/ = undef;
#reading from a file handle now consumes the entire file
}
#But not out here
(旁注:永远不要全局设置$/
。如果你在完成后忘记将其设置回来,或者在恢复之前调用其他代码,它会以微妙和可怕的方式破坏事物。)< / p>
在perl中,最好的做法是在有意义的时候将东西放入潜艇;当它没有意义或不必要地使代码复杂化时,词汇块确保范围;如果你确实需要匿名子程序(通常用于回调或类似),那么你可以做my $subref = sub { . . . };
甚至只是将子声明直接粘贴到函数参数中:do_something(callback => sub { . . . });
注意:另请参阅ysth's answer,了解自行调用匿名潜艇的资源相关优势。
答案 1 :(得分:4)
由于perl提供了词法范围的变量(并且,从5.18开始,词汇命名为subs),因此没有确定范围的原因。
我能想到的唯一理由就是内存管理;如果有问题的子句是一个闭包(引用至少一个外部词法变量),那么sub使用的任何内存都将被完全释放而不是保留以便在下次调用时重用:
$ perl -MDevel::Peek -wle'sub { my $x; Dump $x; $x = 42 }->() for 1..2'
SV = NULL(0x0) at 0x944a88
REFCNT = 1
FLAGS = (PADMY)
SV = IV(0x944a78) at 0x944a88
REFCNT = 1
FLAGS = (PADMY)
IV = 42
$ perl -MDevel::Peek -wle'my $y; sub { $y if 0; my $x; Dump $x; $x = 42 }->() for 1..2'
SV = NULL(0x0) at 0x259d238
REFCNT = 1
FLAGS = (PADMY)
SV = NULL(0x0) at 0x259d220
REFCNT = 1
FLAGS = (PADMY)
虽然如果你不关心记忆,这将是一个缺点。
答案 2 :(得分:3)
这不是闻所未闻,但也不常见。要临时限制变量范围,使用带有my
变量声明的块更为常见:
...
{
my $local_variable;
...
}
答案 3 :(得分:2)
在Javascript中,自调用函数有两个用途:
变量范围。 var
声明被提升到第一个封闭函数的范围或全局范围。因此,
function () {
if (true) {
var foo = 42
}
}
与
相同function () {
var foo
if (true) {
foo = 42
}
}
- 通常是不受欢迎的影响。
表达水平的陈述。有时你需要多个语句来计算某些东西,但是想在表达式中这样做。
largeObject = {
...,
// sum from 1 to 42
sum: (function(n){
var sum = 0;
for(var i = 1; i <= n; i++)
sum += i;
return sum;
})(42),
...,
};
Perl不需要将自调用函数作为作用域机制,因为任何大括号都会引入新的作用域。在语句级别始终允许裸块:
...
my $foo = 10;
{
my $foo = 42;
}
$foo == 10 or die; # lives
Perl减少了对自调用函数的需求,因为do BLOCK
内置函数将语句引入表达式:
%large_hash = (
...,
sum => do {
my $sum = 0;
$sum += $_ for 1 .. 42;
$sum;
},
...,
);
但是,你有时会想要在这样的一个块中进行短路。当return
退出周围的子程序(不是块)时,它在这里非常有用。例如,在memoized函数中:
# moronic cached division by two
my %cache;
sub lookup {
my $key = shift;
return $cache{$key} //= sub {
for (1 .. 100) {
return $_ if $_ * 2 == $key
}
return;
}->();
}