perl精粹解析 - closure 与 静态局部变量
时间:2011-01-05
来源:互联网
一.什么是closure
perl 中有一个概念叫“closure”(闭环,来源于数学):
假如有一个subroutine, 它访问了在其外部声明的私有变量,那么这个subroutine就叫做closure.
例如:
Perl code
这里,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的引用,通常作为回调函数:
Perl code
这里,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作为真正的返回值
C/C++ code
这里假设callback的返回值是total_size,那么find可实现如下:
C/C++ code
此时,要想获取totalsize的值,只需要执行:
C/C++ code
2.改变callback函数的接口
C/C++ code
此时,要想获取totalsize的值,只需要执行:
C/C++ code
对方法一和方法二进行小结:
1. 方法一改变了find函数的接口,而通常find函数是系统提供的库函数,接口无法改变.
2. 方法二更糟糕,由于改变了回调函数get_sum的接口,函数指针SUM的类型也发生了改变(增加了一个bool型参数)
, 导致所有被find所调用到的SUM类型的回调函数都需要改变接口和实现,这对代码的扩展和维护来说简直是个灾难!
相比之下,用perl实现同样的功能更加简洁通用,扩展性更好,对现有的代码影响更小。
用法二 使用闭环变量作为输入,用作函数生成器,来生成不同的函数指针:
Perl code
print_bigger_than在这里相当于一个函数生成器,不同的输入变量可以生成不同的函数指针.
这里生成了一个可以打印出文件大小大于1024字节文件名的回调函数.
用法三 作为静态局部变量使用,提供了c语言静态局部变量的功能:
Perl code
这里用到了关键字BEGIN.
BEGIN的作用就是,当perl编译完这段代码之后,停止当前编译,然后直接进入运行阶段,执行BEGIN块内部的代码.然后再回到编译状态,
继续编译剩余的代码.
这就保证了无论BEGIN块位于程序中的哪个位置,在调用count_down之前,$countdown被确保初始化为10.
perl 中有一个概念叫“closure”(闭环,来源于数学):
假如有一个subroutine, 它访问了在其外部声明的私有变量,那么这个subroutine就叫做closure.
例如:
Perl code
{ 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的引用,通常作为回调函数:
Perl code
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作为真正的返回值
C/C++ code
#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可实现如下:
C/C++ code
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的值,只需要执行:
C/C++ code
int totalsize = 0; find(get_sum, path, pathlen, &totalsize);
2.改变callback函数的接口
C/C++ code
#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的值,只需要执行:
C/C++ code
find(get_sum, path, len); int totalsize = get_sum(anyinteger, true);
对方法一和方法二进行小结:
1. 方法一改变了find函数的接口,而通常find函数是系统提供的库函数,接口无法改变.
2. 方法二更糟糕,由于改变了回调函数get_sum的接口,函数指针SUM的类型也发生了改变(增加了一个bool型参数)
, 导致所有被find所调用到的SUM类型的回调函数都需要改变接口和实现,这对代码的扩展和维护来说简直是个灾难!
相比之下,用perl实现同样的功能更加简洁通用,扩展性更好,对现有的代码影响更小。
用法二 使用闭环变量作为输入,用作函数生成器,来生成不同的函数指针:
Perl code
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语言静态局部变量的功能:
Perl code
BEGIN { my $countdown = 10; sub count_down { $countdown-- } sub count_remaining { $countdown } }
这里用到了关键字BEGIN.
BEGIN的作用就是,当perl编译完这段代码之后,停止当前编译,然后直接进入运行阶段,执行BEGIN块内部的代码.然后再回到编译状态,
继续编译剩余的代码.
这就保证了无论BEGIN块位于程序中的哪个位置,在调用count_down之前,$countdown被确保初始化为10.
作者: mac_philips 发布时间: 2011-01-05
原创?现在很少有代码在调用函数的时候前面加&了,&callback()一般都写callback()。另外“私有变量”并不准确,closure和私有与否没什么联系。准确些讲是局部变量(或者说局部引用)。闭环也是编程里很少用的术语,一般叫闭包比较多。对C语言的比较那一部分也不是很到位,closure是动态语言才有的,最大的优势在于可以动态创建新的closure,这是与C语言最大的区别。
作者: iambic 发布时间: 2011-01-05
相关阅读 更多
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28