• 子例程引用与闭包:


    和普通变量一样,子例程(subroutine)可以是有名的也可以是匿名的,而且Perl语法上支持对任何一种类型的引用。
    
    1. 调度表(dispatch table),它是一种将事件映射到子例程引用的数据结构。
    
    每当事件发生时,就会通过一张调度表来查找与之相关的子例程。
    
    
    2.闭包(Closure),闭包是这样一种子例程,创建时,它将包含其子例程的环境打包(包括所有它需要的和不局部与其本身的变量)。
    
    子例程引用:
    
    对有名子例程的引用:
    
    我们前面就已经讲过,要创建对现存变量的引用,只需给它加上反斜杠前缀。
    
    对于子例程也大致如下,如&mysub就是对&mysub的引用
    
    
    [oracle@oadb 4]$ cat a1.pl 
    sub greet {
       print "hello
    ";
    };
    
    $rs=&greet; ##创建对子例程greet的引用
    
    &$rs;
    
    [oracle@oadb 4]$ perl a1.pl 
    hello
    
    
    [oracle@oadb 4]$ cat a2.pl 
    sub greet {
       print "hello
    ";
    };
    
    $rs=&greet(); ##创建对子例程greet的引用
    
    &$rs;
    
    [oracle@oadb 4]$ perl a2.pl 
    hello
    Not a CODE reference at a2.pl line 7.
    [oracle@oadb 4]$ 
    
    
    对匿名子例程的引用:
    
    你只需省略子例程声明中的过程名即可创建匿名子例程,除了这一点外,其声明同有名字例程完全一样:
    
    [oracle@oadb 4]$ cat a3.pl 
    $rs=sub{
       print "hello 
    ";
    };
    
    print &$rs;
    [oracle@oadb 4]$ perl a3.pl 
    hello 
    1[oracle@oadb 4]$ 
    
    
    对子例程引用的间接访问:
    
    
    对子例程引用的间接访问(dereference)将会间接的调用该子例程。
    
    和数据引用一样,Perl并不关心$rs指向的有名还是无名的子例程。
    
    [oracle@oadb 4]$ cat a4.pl 
    sub greet {
       my $a=shift;
       my $b=shift;
       return $a+$b;
    };
    
    my $rs=&greet;
    
    print &$rs(10,20);
    [oracle@oadb 4]$ perl a4.pl 
    30[oracle@oadb 4]$ 
    
    [oracle@oadb 4]$ cat a4.pl 
    sub greet {
       my $a=shift;
       my $b=shift;
       return $a+$b;
    };
    
    my $rs=&greet;
    
    print $rs->(10,20);
    [oracle@oadb 4]$ perl a4.pl 
    30[oracle@oadb 4]$ 
    
    
    使用子例程引用:
    
    让我们来看一些子例程引用的常见例子:回调函数与高等级子例程:
    
    回调函数就是通过引用来使用的普通子例程。
    
    
    
    
    调度表 
    
    典型的调度表就是一个包含子例程引用的数组
    
    [oracle@oadb 4]$ cat a5.pl 
     %options = (   #针对每个选项调用相应的子例程
         "-h" =>&help,
         "-f" =>sub{$a='abcdef'},
         "-r" =>sub {$b=1}
    );
    
    sub ProcessArgs {
      ##注意记号的意义,r1表示指向数组的引用,rh表示指向散列表的引用
      my $rlArgs=shift;
      my $rhOptions=shift;
      foreach $arg (@$rlArgs){
        if (exists $rhOptions->{$arg}){
             #其值一定是指向子例程的引用
             $rsub = $rhOptions->{$arg};
             print &$rsub;
             print "
    ";
    }
    }
    };
    
    my @arr=("-h","-f");
    
    sub help{
       return 78 + 21;
    };
    
    &ProcessArgs(@arr,\%options);
    
    
        
    [oracle@oadb 4]$ perl a5.pl 
    99
    abcdef
    
    
    
    闭包:
    
    不仅可以返回数据,Perl的子例程还可以返回一个指向子例程的引用。
    
    [oracle@oadb 4]$ cat a6.pl 
    use Data::Dumper;
    $greeting ="hello world";
    $rs=sub {
       print $greeting;
    };
    
    print $rs;
    print "
    ";
    print Dumper($rs);
    print "
    ";
    
    print &$rs;
    [oracle@oadb 4]$ perl a6.pl 
    CODE(0x1b0a660)
    $VAR1 = sub { "DUMMY" };
    
    hello world1[oracle@oadb 4]$ 
    
    在这个例子中,匿名子例程利用了全局变量$greeting
    
    
    [oracle@oadb 4]$ cat a7.pl 
    sub generate_greeting {
       my $greeting="hello world";
       return sub{print $greeting};
    };
    
    $rs=generate_greeting();
    &$rs();
    [oracle@oadb 4]$ perl a7.pl 
    hello world[oracle@oadb 4]$ 
    [oracle@oadb 4]$ 
    
    
    让我们再来改动一下这段代码,以更好地理解闭包这个专业用语究竟能做什么:
    
    [oracle@oadb 4]$ cat a8.pl 
    sub generate_greeting {
        my $greeting = shift;
        return sub {
          my $subject=shift;
          print "$greeting $subject abcdefg
    ";
       }
    };
    
    $rs1=generate_greeting("hello");
    
    print &$rs1;
    print "
    ";
    print &$rs1("world");
    
      
    [oracle@oadb 4]$ perl a8.pl 
    hello  abcdefg
    1
    hello world abcdefg
    1[oracle@oadb 4]$ 
    
    
    闭包的内幕:
    
    闭包的应用:
    
    智能回调
    当用户按下按钮时,回调过程将被调用,并使用它所保存的$title的值。  
      
    use Tk;  
    ##创建顶层窗口  
    my $mw = MainWindow->new(-title => "Mem monitor");  
    ##创建两个按钮,这些按钮将在被按下打印自己的名字  
    CreateButton($mw,"hello");  
      
    CreateButton($mw,"world");  
      
    sub CreateButton {  
       my ($parent,$title)=@_;  
       my $b;  
       $callback_proc=sub {  
               print "Button $title pressed
    ";  
               };  
          
         $frm_name1 = $mw->Frame()->pack(-side=>"top",-fill => 'x');  
        $b=$frm_name1->Button (  
             '-text'=>"title",  
             '-fg'=>'red',  
             '-command'=>$callback_proc  
    );  
        $b->pack();  
    };  
    MainLoop;  
                 
      
    C:UsersTLCBDesktopperl>perl testtk.pl  
    Button hello pressed  
    Button hello pressed  
    Button hello pressed  
    Button world pressed  
    Button world pressed  
    Button world pressed  
    Button world pressed  
    Button world pressed  
    Button world pressed  
      


    
                                        
    
  • 相关阅读:
    基于vue-cli配置移动端自适应项目
    webpack 之 resolve.alias(别名)
    vue 之引用全局样式
    webpack 3.0
    vue 之 data为什么必须声明为返回一个初始数据对象的函数?
    JS柯里化
    《css设计指南》 读书笔记 二
    《css设计指南》 读书笔记 一
    简单的移动端图片预览 包含放大缩小以及对各种手势的判定
    图片拍照上传 使用fileReader 无需跨域
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13349761.html
Copyright © 2020-2023  润新知