和普通变量一样,子例程(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