什麼是閉包,“This is a notion out of the Lisp world that says if you define an anonymous function in a particular lexical context, it pretends to run in that context even when it's called outside of the context.”【2】。在面向對象的語言裏面,“A closure is a callable object that
retains information from the scope in which it was created. From this definition, you can see that an inner class is an object-oriented closure, because it doesn’t just contain each piece of information from the outer-class object ("the scope in which it was
created"), but it automatically holds a reference back to the whole outer-class object, where it has permission to manipulate all the members, even private ones.”【3】
先看這個例子:
#!/usr/bin/perl -w
use strict;
{
my $inc = 10;
sub incr {
print "$inc\n";
$inc++;
}
}
incr();
incr();
#prints:
#10
#11
這個例子說明命名函數默認是全局的,即使是定義在一個block裏面。我們不能引用變量$inc,但是卻可以調用函數。
#!/usr/bin/perl -w
use strict;
sub make_incr {
my $inc = shift;
return sub { print "$inc\n"; $inc++ };
}
my $c1 = make_incr(10);
my $c2 = make_incr(20);
$c1->();
$c2->();
$c1->();
$c2->();
#prints:
#10
#20
#11
#21
這個例子我們看到了,Perl的函數返回其實是一個匿名函數引用,這個就是magic所在了。這個也是Perl如何實現閉包的。#!/usr/bin/perl -w
use strict;
sub exclaim {
my $prefix = shift;
return sub { print "$prefix $_[0]!\n" };
}
my $batman = exclaim('Indeed');
my $robin = exclaim('Holy');
$robin->('Mackerel'); # prints: Holy Mackerel!
$batman->('Robin'); # prints: Indeed Robin!
那麼閉包有什麼作用呢?以下摘自“Learning Perl Objects, References & Modules”的第6章【1】:
用法一 在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" );
my $total_size = &$get_results();
print "total size of bin is $total_size \n";
這段代碼用於計算某個目錄下所包含的所有文件的大小之和.
用法二 使用閉環變量作爲輸入,用作函數生成器,來生成不同的函數指針:
#!/usr/bin/perl -w
use strict;
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.
最後附上一個相當cool的例子,來在“Perl Best Practices”:
# Generate a new sorting routine whose name is the string in $sub_name
# and which sorts on keys extracted by the subroutine referred to by $key_sub_ref
sub make_sorter {
my ( $sub_name, $key_sub_ref ) = @_;
# Create a new anonymous subroutine that implements the sort...
my $sort_sub_ref = sub {
# Sort using the Schwartzian transform...
return map { $_->[0] } # 3. Return original value
sort { $a->[1] cmp $b->[1] } # 2. Compare keys
map { [ $_, $key_sub_ref->() ] } # 1. Extract key, cache with value
@_; # 0. Perform sort on full arg list
};
# Install the new anonymous sub into the caller's namespace
use Sub::Installer;
caller->install_sub( $sub_name, $sort_sub_ref );
return;
}
# and then...
make_sorter( sort_sha => sub { sha512($_) } );
make_sorter( sort_ids => sub { /^ID:(\d+)/ } );
make_sorter( sort_len => sub { length } );
# and later...
@names_shortest_first = sort_len(@names);
@names_digested_first = sort_sha(@names);
@names_identity_first = sort_ids(@names);
參考:
- http://blog.csdn.net/mac_philips/article/details/6058946
- http://unlser1.unl.csi.cuny.edu/faqs/perl-faq/Q3.14.html
- Think in Java, 4th
- http://www.itworld.com/nl/perl/08302001/
- http://docstore.mik.ua/orelly/perl/advprog/ch04_03.htm