一.什么是closure
perl 中有一个概念叫“closure”(闭环,来源于数学):
假如有一个subroutine, 它访问了在其外部声明的私有变量,那么这个subroutine就叫做closure.
例如:
{
my $count = 0;
sub callback { print ++$count; };
sub get_count{return $count;};
}
&callback(); //此时, $count = 1
&callback(); //此时, $count = 2
这里,callback和get_count就是一个closure,因为它访问了在其外部声明的私有变量$count.
closure在实际应用中提供了c语言中局部静态变量的功能。
它有以下特性:
1.访问的变量是私有变量,保证只有少数subroutine能访问到该变量,这些具有访问权限的subroutine就构成了一个环,因此也叫闭环;
在上面的例子中,$callback和get_count构成了能访问$count的环;
由于环中的subroutine都可以访问$count(与c语言中的静态变量作用一样),使得每一次访问都可以改变$count的值。
在这个例子中,第一次调用&callback()后, $count的值变为1,第2次调用后,值变为2.
2.该私有变量的生命周期是与环中的生命周期最长的subroutine一样。这是通过perl中的引用计数机制来实现的:
上面的例子中,在括号内的代码部分, $count的引用计数是3,出了括号,$count的引用计数变成了2,
callback生命周期结束的时候,$count的引用计数减去1,此时变为1
get_count生命周期结束的时候,$count的引用计数减去1,此时变为0
当$count的引用计数变为0之后,被perl回收。
二.perl中的closure与C语言中静态局部变量的区别
1.C语言本身提供了静态局部变量机制--static关键字,程序员在使用该功能特性的时候更方便;
perl本身要实现同样的功能,需要程序员额外的努力,在第三部分perl中closure的常见用法中,
大家就可以看到这些技巧。
2. C语言中,只有静态局部变量所在的函数对该变量具有访问权;
而perl中,闭环中的所有subroutine都可以访问和修改“静态变量”。
perl的这个特性,使得它在某些情况下,比c语言更加便利。参见下节中的用法一。
三.perl中closure的常见用法
以下摘自“Learning Perl Objects, References & Modules” 的第6章:
用法一 在subroutine中返回subroutine的引用,通常作为回调函数:
use File::Find;
sub create_find_callbacks_that_sum_the_size {
my $total_size = 0;
return(sub { $total_size += -s if -f }, sub { return $total_size });
}
my ($count_em, $get_results) = create_find_callbacks_that_sum_the_size( );
find($count_em, "bin"); //寻找bin目录下所有的文件,并对找到的所有文件执行回调函数$count_em
my $total_size = &$get_results( );
print "total size of bin is $total_size/n"
这段代码用于计算某个目录下所包含的所有文件的大小之和.
这里,create_find_callbacks_that_sum_the_size返回了两个subroutine的引用,一个用来计算
total_size的值,一个用来获取total_size的值.
create_find_callbacks_that_sum_the_size相当于函数生成器,生成了两个subroutine。
如果用c语言来实现同样的功能,有两种方法:
1.改变find函数的接口,增加参数total_size,当find函数返回时,设置total_size作为真正的返回值
#define int (* SUM)(int);
int get_sum(int filesize);
{
static int count = 0;
count += filesize;
return count;
}
bool find(SUM callback, char *path, int pathLen, int *total_size )
这里假设callback的返回值是total_size,那么find可实现如下:
bool find(SUM callback, char *path, int pathLen, int *total_size )
{
bool result = false;
for(...) //寻找path下的每一个文件
{
...
if (file is find)
{
total_size = callback(filesize); //get_sum取代形参callback
result = true;
}
}
return result;
}
此时,要想获取totalsize的值,只需要执行:
int totalsize = 0;
find(get_sum, path, pathlen, &totalsize);
2.改变callback函数的接口
#define int (* SUM)(int, bool);
int get_sum(int filesize, bool ret = false);
{
static int count = 0;
if (!ret)
{
count += filesize;
}
return count;
}
bool find(SUM callback, char *path, int pathLen)
{
bool result = false;
for(...) //寻找path下的每一个文件
{
...
if (file is find)
{
callback(filesize); //get_sum取代形参callback
result = true;
}
}
return result;
}
此时,要想获取totalsize的值,只需要执行:
find(get_sum, path, len);
int totalsize = get_sum(anyinteger, true);
对方法一和方法二进行小结:
1. 方法一改变了find函数的接口,而通常find函数是系统提供的库函数,接口无法改变.
2. 方法二更糟糕,由于改变了回调函数get_sum的接口,函数指针SUM的类型也发生了改变(增加了一个bool型参数)
, 导致所有被find所调用到的SUM类型的回调函数都需要改变接口和实现,这对代码的扩展和维护来说简直是个灾难!
相比之下,用perl实现同样的功能更加简洁通用,扩展性更好,对现有的代码影响更小。
用法二 使用闭环变量作为输入,用作函数生成器,来生成不同的函数指针:
use File::Find;
sub print_bigger_than {
my $minimum_size = shift;
return sub { print "$File::Find::name/n" if -f and -s >= $minimum_size };
}
my $bigger_than_1024 = print_bigger_than(1024);
find($bigger_than_1024, "bin");
print_bigger_than在这里相当于一个函数生成器,不同的输入变量可以生成不同的函数指针.
这里生成了一个可以打印出文件大小大于1024字节文件名的回调函数.
用法三 作为静态局部变量使用,提供了c语言静态局部变量的功能:
BEGIN {
my $countdown = 10;
sub count_down { $countdown-- }
sub count_remaining { $countdown }
}
这里用到了关键字BEGIN.
BEGIN的作用就是,当perl编译完这段代码之后,停止当前编译,然后直接进入运行阶段,执行BEGIN块内部的代码.然后再回到编译状态,
继续编译剩余的代码.
这就保证了无论BEGIN块位于程序中的哪个位置,在调用count_down之前,$countdown被确保初始化为10.
本文深入探讨了Perl中的closure概念,解释了它如何提供类似于C语言静态局部变量的功能。通过示例展示了closure如何在perl中实现私有变量的访问,并对比了与C语言中静态局部变量的区别。此外,还介绍了Perl中closure的两种常见用法,包括在子程序中返回子程序引用作为回调函数以及使用闭包变量生成不同函数指针。
1214

被折叠的 条评论
为什么被折叠?



