zoukankan      html  css  js  c++  java
  • Perl中的闭包(closure)

    什么是闭包,“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);
    

    参考:

    1. http://blog.csdn.net/mac_philips/article/details/6058946
    2. http://unlser1.unl.csi.cuny.edu/faqs/perl-faq/Q3.14.html
    3. Think in Java, 4th
    4. http://www.itworld.com/nl/perl/08302001/
    5. http://docstore.mik.ua/orelly/perl/advprog/ch04_03.htm
  • 相关阅读:
    话说地址栏的URL的最大长度
    程序员,我拿什么来拯救自己
    把女友升级为老婆的时候发生的BUG
    一个精典asp程序引发的错误引起的思考
    [转]提高 Web Service 数据传输效率的基本方法
    整理发布html的select控件实用js操作
    asp.net简单实现导出excel报表
    c#简单实现生成csv文件
    利用sql server直接创建日历
    jQuery学习笔记:效果
  • 原文地址:https://www.cnblogs.com/ainima/p/6331302.html
Copyright © 2011-2022 走看看