sub()函数主要用来声明子过程,具体使用方法如下:
命名声明
sub NAME PROTO ATTRS
sub NAME ATTRS
sub NAME PROTO
sub NAME命名定义
sub NAME PROTO ATTRS BLOCK
sub NAME ATTRS BLOCK
sub NAME PROTO BLOCK
sub NAME BLOCK未命名定义
sub PROTO ATTRS BLOCK
sub ATTRS BLOCK
sub PROTO BLOCK
sub BLOCK子过程声明和定义的语法看起来挺复杂的,但是在实践上实际相当简单。所有东西都是基于下面语法的:
sub NAME PROTO ATTRS BLOCK所有的四个域都是可选的;唯一的限制就是如果这些域的确存在的话那么它们必须以这些顺序出现,并且你必须至少使用 NAME 或者 BLOCK 之一。目前,我们会忽略 PROTO 和 ATTRS;它们只是基本语法的修饰词。NAME 和 BLOCK 都是保证正确重要部分:
如果你只有 NAME 而没有 BLOCK,它就是一个该名字的声明(并且如果你想调用该子过程,那么你就必须稍后用 NAME 和 BLOCK 提供一个定义。)命名的声明是非常有用的,因为如果编译器知道它是一个用户定义子过程,那么它会对该名字另眼相看。你可以把这样的子过程当作一个函数或者当作一个操作符来调用,就象内建函数一样。有时候我们把这样的东西叫做提前声明。
如果你同时提供了 NAME 和 BLOCK,那么它就是一个标准的命名子过程定义(如果你在前面没有声明,那么它还是声明)。命名定义也很重要,因为 BLOCK 把一个实际的含义(子过程体)和声明关联起来。这就是我们所谓的定义和声明的区别。不过,定义和声明也有类似的地方,那就是子过程代码看不到它,并且它不返回你可以用之来引用子过程的内联的值。
如果你只有 BLOCK 而没有 NAME,那么它就是一个匿名的定义,也就是一个匿名子过程。因为它没有名字,所以它根本就不是声明,而是一个真正的操作符,在运行时返回一个指向匿名子过程体的引用。这个东西在把代码当作数据对待的时候极为有用。它允许你传递一段奇怪的代码用做回调函数,并且甚至还可以当作闭合块用——如果该 sub 定义操作符提到了任何在其自身以外的词法变量的话。这就意味着对同样 sub 操作符的不同调用都将进行记录工作,以保证在闭合块的生命期里,每个这样的词法变量的正确的“版本”都是该闭合块可见的,甚至于该词法变量所属的最初的范围被摧毁了也如此。
在上面三种情况中的任何一种里,PROTO 和 ATTRS 之一或者全部都可以在 NAME 之后和/或 BLOCK 之前出现。原型是一个放在圆括弧里的字符列表,它告诉分析器如何对待该函数的参数。属性是用一个冒号引入的,它告诉分析器有关这个函数的额外的信息。下面是一个包含四个域的典型的定义:
sub numstrcmp ($$) : locked {
my ($a, %b) = @_;
return $a <=> $b || $a cmp $b;
}
评论
0评论加载中…