perl精粹解析 - closure 与 静态局部变量
一.什么是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; ???? 这里,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. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |