加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

perl精粹解析 - closure 与 静态局部变量

发布时间:2020-12-15 20:51:39 所属栏目:大数据 来源:网络整理
导读:一.什么是closure perl 中有一个概念叫“closure”(闭环,来源于数学): 假如有一个subroutine,它访问了在其外部声明的私有变量,那么这个subroutine就叫做closure. ? 例如: { ? my $count = 0; ? sub callback { print ++$count; }; ? sub get_count{ret

一.什么是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.


(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读