📜  Perl – 闭包函数

📅  最后修改于: 2022-05-13 01:55:35.337000             🧑  作者: Mango

Perl – 闭包函数

闭包是 Perl 中的重要概念之一。闭包是一个计算机术语,具有准确但难以解释的含义。它通常被称为可以存储为变量的函数,它具有访问创建它的范围内的其他本地变量的特殊能力。它用于引用任何外部函数的局部变量,并且当编译器检测到这一点,它将这些变量从外部函数的堆栈空间移动到闭包的隐藏对象声明中。然后它生成一个闭包类型的变量。
所以基本上,一个捕获它被创建的词法环境的函数——即它运行收集当时环境中的所有东西的函数被称为闭包函数。

关于 Perl 语言的闭包意味着:子程序 1 返回某个其他子程序 2,然后子程序 2 访问子程序 1 中存在的变量 x,当子程序 1 的执行结束时,用户不能再访问 x,但子程序2 因为也指变量x指向数据对象。它使子程序 2 在子程序 1 结束后继续访问该数据对象。
因此,子程序1中的变量x必须是词法变量,否则子程序1执行后,变量x仍有可能被外界访问,修改的话,闭包和普通函数就没有意义了。

闭包是一种绑定到特定环境的函数类型,并被编写为另一个函数内部的函数。

例子:

sub make_adder 
{
    my $addpiece = shift;
    return sub { shift() + $addpiece };
}
  
# Driver Code
my $f1 = make_adder(555);
my $f2 = make_adder(20);

现在 $f1($n) 总是 555 加上你传入的任何 $n,而 $f2($n) 总是 20 加上你传入的任何 $n。闭包中的 $addpiece 一直存在。

闭包的使用

闭包是非常有用的东西。例如,对于回调,无需在函数中传递回调函数指针 + 参数,并且不必在该函数中计算出您被回调的内容,只需使用闭包即可。当该函数被调用时,它可以访问它创建时的所有词法环境。

闭包的一些主要用途是:将它们用作“智能”回调,另一种是将它们用作迭代器。

  • 智能回调函数
    例如:我们想要创建一个按钮(使用 Tk 工具包)给它一个子程序引用。但是如果我们想给屏幕上的 2 个不同的按钮一个子程序,那将是一个问题。因此,为了做到这一点,我们将使用一个“智能”回调例程,即闭包。这将使闭包存储一些特定于该按钮的数据(如其名称),并且当调用该子例程时,它可以访问该数据。
  • 迭代器
    迭代器通常是一个代码引用,在执行时,它会计算列表中的下一项并返回它。
    例如:假设有一个偶数流,上面有一个迭代器,它在调用时返回下一个偶数。显然,不可能生成所有可能的偶数,但如果它记得之前生成的数字,它总是可以计算下一个偶数。这条关键信息被迭代器记住了。
  • 闭包的另一个用途是它可以使变量成为命名子例程的私有变量。
    例如:在子创建时初始化的计数器,只能从子内部修改。这有时可以与包文件中的 BEGIN 块一起使用,以确保变量在包的生命周期内不会干扰。

部分应用的使用

部分应用函数被称为具有许多参数的函数并将参数应用于其中一些参数以创建一个新函数的能力,该函数只需要应用剩余的参数来产生将参数应用于原始函数的等价物。
部分应用的反复出现的特点是,当它应用于一个参数时,不应该明确提及其他参数。

例子:

sub fs(&) 
{ 
    my $func = shift;
    sub { map $func->($_), @_ }
}
    
sub double($) { shift() * 2 }
sub square($) { shift() ** 2 }
    
my $fs_square = fs(\&square);
my $fs_double = fs(\&double);
    
my @s = 0 .. 3;
print "fs_square(@s): @{[ $fs_square->(@s) ]}\n";
print "fs_double(@s): @{[ $fs_double->(@s) ]}\n";
    
@s = (2, 4, 6, 8);
print "fs_square(@s): @{[ $fs_square->(@s) ]}\n";
print "fs_double(@s): @{[ $fs_double->(@s) ]}\n";

输出:

fs_square(0 1 2 3): 0 1 4 9
fs_double(0 1 2 3): 0 2 4 6
fs_square(2 4 6 8): 4 16 36 64
fs_double(2 4 6 8): 4 8 12 16