和普通变量一样,子例程(subroutine) 可以是有名的也可以是匿名的,而且Perl 语法上支持对任何一种类型的引用。
对有名子例程的引用:
我们前面已经讲过,要创建对现存变量的引用,只需给它加上反斜杠前缀。对子例程也大致如此,
如&mysub 就是对&mysub的引用,又比如:
zjzc01:/root/hei# cat a24.pl
sub greet {
print "hello
";
};
$rs = &greet; #创建对子例程greet的引用
&$rs;
zjzc01:/root/hei# perl a24.pl
hello
zjzc01:/root/hei# cat a24.pl
sub greet {
print "hello
";
};
$rs = &greet;
&$rs;
$var=&$rs;
print $var;
print "
";
zjzc01:/root/hei# perl a24.pl
hello
hello
1
该表达式的结果可能不是你所希望的,它调用greet,然后创建了一个对其返回值的引用
也就是子例程中最后一个表达式的值是引用。由于print 最后执行并返回1或0
(表明是否成功打印了后面的值),因此该表达式的结果就是一个对值为1或者0的标量变量的引用
对匿名子例程的引用:
你只需要省略掉子例程声明中的过程名即可创建匿名子例程。除了这一点外,其声明同有名字例程完全一样:
zjzc01:/root/hei# cat a25.pl
$rs = sub{print "hello
";};
$var= &$rs;
print "111111111
";
print $var;
print "
";
zjzc01:/root/hei# perl a25.pl
hello
111111111
1
zjzc01:/root/hei# cat a25.pl
print sub{print "hello
";};
print "
";
print &{sub{print "hello
";}};
print "
";
zjzc01:/root/hei# perl a25.pl
CODE(0x244a2f0)
hello
1
$client->on_receive_msg = sub{
my $msg = shift ;
zjzc01:/root/hei# cat a25.pl
$var= sub{print "hello
";};
print $var;
print "
";
print &$var;
print "
";
$x=&$var; print "$x is $x
";
zjzc01:/root/hei# perl a25.pl
CODE(0xa38320)
hello
1
hello
$x is 1
对子例程引用的间接访问:
对子例程引用的间接访问(dereference) 将会间接的调用该子例程。
和数据引用一样,Perl并不关心$rs指向的有名还是无名的子例程,间接访问操作对两种情况都适合;
使用子例程引用:
让我们来看一些子例程引用的常见例子:回调函数与高级子例程
my (@frames,@button);
my %sub_of = (
"日志查询" => &push_button2 ,
"温金短信查询" => &push_button3 ,
"中均短信查询" => &push_button4 ,
"机器信息查询" => &push_button5,
"IP信息查询" => &push_button6,
"设备信息维护" => &push_button7,
"test" => sub{ print "program 6" },
7 => sub{ print "program 7" }, ##函数引用
8 => sub{ print "program 8" },
9 => sub{ print "program 9" },
);
~
回调函数就是通过引用来使用的普通子例程,调用者(那些引用使用者)
调度表:
典型的调度表就是一个包含子例程引用的数组,下面的例子中,%options 就是一个将命令行选项映射到
不同字例程的调度表:
%options = ( ##针对每个选项调用相应的子例程
"-h" =>&help;
"-f" =>sub{$askNoQuestions=1},
"-r" =>sub{$recursive=1},
"_default_" => &default.
);
闭包:
不仅可以返回数据,Perl的子例程还可以返回一个指向子例程的引用。
这同其他别的传递子例程引用的方法没有什么不同,但是有一点是个例外,这是一种涉及匿名子例程和
词法变量(my) 的隐含特性。考虑下面的例子:
zjzc01:/root/hei# cat a26.pl
$greeting = "hello world";
$rs = sub{ print $greeting;};
&$rs();
print "
";
print "1111111111
";
$var=$rs;
print $var;
print "
";
print &$var;
print "
";
zjzc01:/root/hei# perl a26.pl
hello world
1111111111
CODE(0x718368)
hello world1
----------------------------------
zjzc01:/root/hei# cat a26.pl
$greeting = "hello world";
$rs = sub{ return $greeting;};
&$rs();
print "
";
print "1111111111
";
$var=$rs;
print $var;
print "
";
print &$var;
print "
";
zjzc01:/root/hei# perl a26.pl
1111111111
CODE(0x1795368)
hello world
zjzc01:/root/hei# cat a27.pl
sub generate_greeting{
my ($greeting) = "hello world";
return sub{print $greeting}; ##返回引用
};
$rs=&generate_greeting;
print $rs;
print "
";
print &$rs;
print "
";
zjzc01:/root/hei# perl a27.pl
CODE(0xeded48)
hello world1
闭包的应用:
闭包以两种似乎毫无相关的方式来应用。最常见的方式就是用"智能调用"回调子例程
由于闭包是一些包含少量私有数据的子例程引用,因此它们可以方便的应用在图形用户界面的回调过程中:
闭包与对象的对比:
对象通俗的定义是一组数据与函数的集合,数据提供对象函数操作的环境。
比如,$buttion->setForeground("yellow")中, setForeground 函数会自动知道你指的是哪个按钮
从某种意义上说,闭包也提供了同样的机制---它也是一种子例程与只对子例程可见的私有数据的结合。