一.什么是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,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,int pathLen)
{
bool result = false;
for(...) //寻找path下的每一个文件
{
...
if (file is find)
{
callback(filesize); //get_sum取代形参callback
result = true;
}
}
return result;
}
此时,要想获取totalsize的值,只需要执行:
find(get_sum,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.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。