perlsub - 云端在线

这是可以在 OnWorks 免费托管服务提供商中使用我们的多个免费在线工作站之一运行的命令 perlsub,例如 Ubuntu Online、Fedora Online、Windows 在线模拟器或 MAC OS 在线模拟器

程序:

您的姓名


perlsub - Perl 子程序

概要


声明子程序:

子名称; # 一个“转发”声明。
子名称(原型); # 同上,但有原型
子名称:ATTRS; # 有属性
子名称(原型):属性; # 带有属性和原型

sub NAME BLOCK # 一个声明和一个定义。
sub NAME(PROTO) BLOCK # 同上,但有原型
sub NAME(SIG) BLOCK # 用签名代替
子名称:ATTRS BLOCK # 带属性
sub NAME(PROTO) : ATTRS BLOCK # 带有原型和属性
sub NAME(SIG) : ATTRS BLOCK # 带有签名和属性

在运行时定义匿名子程序:

$subref = 子块; # 没有原型
$subref = sub (PROTO) 块; # 使用原型
$subref = sub(SIG)块; # 有签名
$subref = sub :ATTRS 块; # 有属性
$subref = sub (PROTO) : ATTRS BLOCK; # 带有原型和属性
$subref = sub (SIG) : ATTRS BLOCK; # 带有签名和属性

导入子程序:

使用 MODULE qw(NAME1 NAME2 NAME3);

调用子程序:

名单); # & 是可选的,带括号。
名单; # 括号可选,如果预先声明/导入。
&名单); # 绕过原型。
&名称; # 使当前@_ 对被调用的子程序可见。

商品描述


像许多语言一样,Perl 提供用户定义的子程序。 这些可能位于
主程序中的任何位置,通过“do”、“require”或“use”从其他文件加载
关键字,或使用“eval”或匿名子程序动态生成。 你甚至可以
使用包含其名称或 CODE 引用的变量间接调用函数。

函数调用和返回值的 Perl 模型很简单:所有函数都作为
参数 一个单一的标量列表,所有函数同样返回到它们的
调用者一个单一的标量列表。 这些调用和返回中的任何数组或散列
列表会崩溃,失去它们的身份——但你可能总是使用传递引用
而是为了避免这种情况。 调用列表和返回列表都可以包含或多或少的标量
元素随心所欲。 (通常调用没有显式返回语句的函数
一个子程序,但从 Perl 的角度来看确实没有区别。)

传入的任何参数都显示在数组@_ 中。 (它们也可能出现在词法中
由签名引入的变量; 请参阅下面的“签名”。)因此,如果您致电
带有两个参数的函数,它们将存储在 $_[0] 和 $_[1] 中。 数组@_ 是一个
本地数组,但其元素是实际标量参数的别名。 在
特别地,如果一个元素 $_[0] 被更新,则相应的参数被更新(或
如果它不可更新,则会发生错误)。 如果参数是数组或散列元素
调用函数时不存在,该元素仅在(如果)它时创建
被修改或引用它。 (一些早期版本的 Perl 创建了
element 元素是否已分配给。)分配给整个数组@_
删除该别名,并且不更新任何参数。

“return”语句可用于退出子程序,可选择指定返回的
值,将在适当的上下文(列表、标量或空值)中进行评估
取决于子程序调用的上下文。 如果不指定返回值,则
子程序在列表上下文中返回一个空列表,在标量上下文中返回未定义的值,
或在无效上下文中什么都没有。 如果您返回一个或多个聚合(数组和散列),
这些将被合并为一个无法区分的大列表。

如果没有找到“return”并且最后一个语句是一个表达式,则返回它的值。
如果最后一条语句是循环控制结构,如“foreach”或“while”,则
返回值未指定。 空子返回空列表。

除了一个实验设施(见下面的“签名”),Perl 没有命名
形式参数。 实际上,您所做的就是分配给这些的“my()”列表。 变量
未声明为私有的是全局变量。 有关创建的血腥细节
私有变量,参见“私有变量通过 我的()" 和 "临时值通过 当地的()".
为单独包中的一组函数创建受保护的环境(和
可能是一个单独的文件),请参阅 perlmod 中的“包”。

示例:

次最大{
我的 $max = shift(@_);
foreach $foo (@_) {
$max = $foo 如果 $max < $foo;
}
返回 $max;
}
$bestday = max($mon,$tue,$wed,$thu,$fri);

示例:

# 获取一行,合并连续行
# 以空格开头

子get_line {
$thisline = $lookahead; # 全局变量!
线:而(定义($lookahead = )){
如果($lookahead =~ /^[ \t]/){
$thisline .= $lookahead;
}
其他{
最后一行;
}
}
返回 $thisline;
}

$lookahead = ; # 获取第一行
而(定义($line = get_line())){
...
}

分配给私有变量列表以命名您的参数:

子集{
我的($key, $value) = @_;
$Foo{$key} = $value 除非 $Foo{$key};
}

因为赋值复制了值,所以这也有将 call-by-
引用到按值调用。 否则,函数可以自由地进行就地修改
@_ 并更改其调用者的值。

upcase_in($v1, $v2); # 这会改变 $v1 和 $v2
子 upcase_in {
对于 (@_) { tr/az/AZ/}
}

当然,您不能以这种方式修改常量。 如果一个论点是
实际上是字面意思,并且您尝试更改它,您会采取(可能是致命的)异常。
例如,这行不通:

upcase_in(“弗雷德里克”);

如果编写“upcase_in()”函数来返回其副本的副本会更安全
参数而不是就地更改它们:

($v3, $v4) = upcase($v1, $v2); # 这不会改变 $v1 和 $v2
子大写{
除非定义了想要的数组,否则返回; # 空上下文,什么都不做
我的@parms = @_;
对于(@parms){tr/az/AZ/}
返回想要数组? @parms : $parms[0];
}

注意这个(非原型的)函数是如何不关心它是通过真实标量还是
数组。 Perl 将@_ 中的所有参数视为一个大的、长的、扁平的参数列表。 这是一
Perl 简单的参数传递风格闪耀的领域。 “upcase()”函数将
即使我们喂它东​​西,也不会改变“upcase()”定义的情况下工作得很好
喜欢这个:

@newlist = 大写(@list1,@list2);
@newlist = 大写(拆分/:/,$var);

但是,不要试图这样做:

(@a, @b) = 大写(@list1, @list2);

与扁平化的传入参数列表一样,返回列表也在返回时扁平化。
因此,您在这里所做的就是将所有内容存储在@a 中并将@b 设为空。 看
替代方案的“通过引用传递”。

可以使用显式“&”前缀调用子程序。 “&”在现代是可选的
Perl,如果子例程已经预先声明,则括号也是如此。 “&”是 而不去 可选
当只是命名子程序时,例如当它用作参数时 定义() or
取消定义(). 当你想用一个间接的子程序调用时它也不是可选的
使用“&$subref()”或“&{$subref}()”结构的子例程名称或引用,尽管
“$subref->()”符号解决了这个问题。 有关这一切的更多信息,请参阅 perlref。

子程序可以递归调用。 如果使用“&”形式调用子程序,则
参数列表是可选的,如果省略,则不会为子程序设置@_ 数组:
@_ 数组在调用时对子程序可见。 这是一种效率
新用户可能希望避免的机制。

&foo(1,2,3); # 传递三个参数
富(1,2,3); # 相同

富(); # 传递一个空列表
&foo(); # 相同

&foo; # foo() 获取当前参数,比如 foo(@_) !!
富; # like foo() IFF sub foo 预先声明,否则为“foo”

"&" 形式不仅使参数列表成为可选的,它还禁用了任何原型
检查您提供的参数。 这部分是由于历史原因,部分是
如果您知道自己在做什么,就可以方便地作弊。 参见“原型”
联络一位教师

从 Perl 5.16.0 开始,“__SUB__”标记在“use feature 'current_sub'”下可用,并且
“使用 5.16.0”。 它将评估为对当前运行的 sub 的引用,这允许
用于在不知道子程序名称的情况下进行递归调用。

使用 5.16.0;
我的 $factorial = sub {
我的 ($x) = @_;
如果 $x == 1 则返回 1;
返回($x * __SUB__->( $x - 1 ) );
};

“__SUB__”在正则表达式代码块(例如“/(?{...})/”)中的行为受
改变。

名称全部为大写的子例程保留给 Perl 核心,就像
名称全部小写的模块。 所有大写的子程序都是松散的
约定意味着它将被运行时系统本身间接调用,通常是由于
到触发事件。 名称以左括号开头的子程序也是
以同样的方式保留。 以下是目前做的一些子程序的列表
特殊的、预先定义的东西。

本文档稍后记录
“自动加载”

在 perlmod 中记录
"CLONE", "CLONE_SKIP",

在 perlobj 中记录
“破坏”

记录在 perltie
“BINMODE”、“CLEAR”、“CLOSE”、“DELETE”、“DESTROY”、“EOF”、“EXISTS”、“EXTEND”、“FETCH”、
"FETCHSIZE", "FILENO", "FIRSTKEY", "GETC", "NEXTKEY", "OPEN", "POP", "PRINT",
"PRINTF", "PUSH", "READ", "READLINE", "SCALAR", "SEEK", "SHIFT", "SPLICE", "STORE",
“STORESIZE”、“TELL”、“TIEARRAY”、“TIEHANDLE”、“TIEHASH”、“TIESCALAR”、“UNSHIFT”、
“解开”,“写”

记录在 PerlIO::via
"BINMODE", "CLEARERR", "CLOSE", "EOF", "ERROR", "FDOPEN", "FILENO", "FILL", "FLUSH",
“打开”、“弹出”、“推送”、“阅读”、“搜索”、“SETLINEBUF”、“系统打开”、“告诉”、“未阅读”、
“UTF8”,“写入”

记录在 perlfunc
“导入”,“取消导入”,“INC”

记录在 UNIVERSAL
“版本”

记录在 perldebguts
“DB::DB”、“DB::sub”、“DB::lsub”、“DB::goto”、“DB::postponed”

未记录,由重载功能内部使用
任何以“(”开头

“BEGIN”、“UNITCHECK”、“CHECK”、“INIT”和“END”子程序没有那么多
子程序作为命名的特殊代码块,在一个代码块中可以有多个
包,你可以 而不去 明确调用。 请参阅“开始、取消检查、检查、初始化和
结束”在 perlmod

签名
警告: 子程序签名是实验性的。 该功能可能会被修改或删除
在 Perl 的未来版本中。

Perl 有一个实验工具允许子程序的形式参数
由特殊语法引入,与子程序主体的过程代码分开。
形式参数列表被称为 签名. 必须首先通过以下方式启用该设施
一个务实的声明,“使用特征‘签名’”,它将产生一个警告,除非
“experimental::signatures”警告类别被禁用。

签名是子程序主体的一部分。 通常子程序的主体很简单
一个带支撑的代码块。 使用签名时,签名为带括号的列表
紧跟在子程序名称之后(或者,对于匿名子程序,
紧跟在“sub”关键字之后)。 签名声明的词法变量是
在块的范围内。 当子程序被调用时,签名首先取得控制权。
它从传递的参数列表中填充签名变量。 如果
参数列表不符合签名的要求,那么它会抛出一个
例外。 当签名处理完成时,控制权传递给块。

位置参数通过简单地在签名中命名标量变量来处理。 为了
例,

子 foo ($left, $right) {
返回 $left + $right;
}

采用两个位置参数,必须在运行时由两个参数填充。 经过
默认参数是强制性的,并且不允许传递超过
预期的。 所以上面的等价于

子 foo {
除非@_ <= 2,否则“子程序的参数太多”;
除非@_ >= 2,否则“子程序的参数太少”;
我的 $left = $_[0];
我的 $right = $_[1];
返回 $left + $right;
}

可以通过从参数中省略名称的主要部分来忽略参数
声明,只留下一个裸露的“$”符号。 例如,

子 foo ($first, $, $third) {
返回“第一=$第一,第三=$第三”;
}

尽管忽略的参数不会进入变量,但它仍然是强制性的
调用者传递它。

位置参数是可选的,通过提供一个默认值,与
参数名称由“=”:

子 foo ($left, $right = 0) {
返回 $left + $right;
}

可以使用一个或两个参数调用上述子程序。 默认值
在调用子程序时计算表达式,因此它可能提供不同的默认值
不同调用的值。 仅当实际省略参数时才对其进行评估
从通话中。 例如,

我的 $auto_id = 0;
子 foo ($thing, $id = $auto_id++) {
打印 "$thing 有 ID $id";
}

自动为没有提供 ID 的事物分配不同的顺序 ID
呼叫者,召集者。 默认值表达式也可以引用前面的参数
签名,使一个参数的默认值根据较早的参数而有所不同。
例如,

sub foo ($first_name, $surname, $nickname = $first_name) {
print "$first_name $surname 被称为\"$nickname\"";
}

可选参数可以像强制参数一样是无名的。 例如,

子 foo ($thing, $ = 1) {
打印 $thing;
}

如果相应的参数不是,则仍将评估参数的默认值
提供,即使该值不会存储在任何地方。 这是在评估它的情况下
有重要的副作用。 但是,它将在空上下文中进行评估,因此如果它
没有副作用并且不是微不足道的,如果“无效”,它会产生警告
警告类别已启用。 如果一个无名可选参数的默认值不是
重要的是,它可以被省略,就像参数的名称一样:

子 foo ($thing, $=) {
打印 $thing;
}

可选位置参数必须在所有强制位置参数之后。 (如果
没有强制性的位置参数,那么可选的位置参数可以是
签名中的第一件事。)如果有多个可选的位置参数
并且没有提供足够的参数来填充它们,它们将从左到
对。

在位置参数之后,额外的参数可能会被捕获在一个 slurpy 参数中。
最简单的形式就是一个数组变量:

子 foo ($filter, @inputs) {
打印 $filter->($_) foreach @inputs;
}

签名中有一个slurpy参数,参数数量没有上限
可能会通过。 一个slurpy数组参数可能就像一个位置参数一样是无名的,
在这种情况下,它唯一的作用是关闭参数限制,否则
应用:

子 foo ($thing, @) {
打印 $thing;
}

一个 slurpy 参数可能是一个散列,在这种情况下,它可用的参数是
解释为交替的键和值。 必须有与值一样多的键:如果
有一个奇怪的参数,然后将抛出异常。 键将被字符串化,并且
如果有重复,则后面的实例优先于前面的实例,就像
标准哈希构造。

子 foo ($filter, %inputs) {
打印 $filter->($_, $inputs{$_}) foreach 排序键 %inputs;
}

一个模糊的哈希参数可能就像其他类型的参数一样是无名的。 它仍然
坚持认为它可用的参数数量是偶数,即使它们不是
被放入一个变量中。

子 foo ($thing, %) {
打印 $thing;
}

slurpy 参数,无论是数组还是散列,都必须是签名中的最后一件事。 有可能
遵循强制性和可选的位置参数; 它也可能是唯一的东西
签名。 Slurpy 参数不能有默认值:如果没有为
然后你会得到一个空数组或空散列。

签名可能完全为空,在这种情况下,它所做的只是检查调用者
不通过参数:

子富(){
123返回;
}

使用签名时,参数在特殊数组变量中仍然可用
@_,除了签名的词法变量。 之间有区别
访问参数的两种方式:@_ 别名 参数,但签名
变量获取 副本 的论点。 所以写入签名变量只会改变
该变量,对调用者的变量没有影响,但写入
@_ 修改调用者用来提供该参数的任何内容。

签名和原型之间存在潜在的句法歧义(参见
“原型”),因为两者都以左括号开头,并且都可以出现在某些
相同的地方,例如就在子程序声明中的名称之后。 为了
历史原因,当未启用签名时,此类中的任何左括号
上下文将触发非常宽容的原型解析。 大多数签名将是
在这些情况下被解释为原型,但不会是有效的原型。 (一个有效的
原型不能包含任何字母字符。)这会导致一些混乱
错误信息。

为避免歧义,当启用签名时,原型的特殊语法是
禁用。 没有尝试猜测括号内的组是否旨在成为
原型或签名。 要在这些情况下为子程序提供原型,请使用
原型属性。 例如,

子 foo :prototype($) { $_[0] }

子程序完全有可能同时拥有原型和签名。 他们
做不同的工作:原型影响对子程序调用的编译,以及
签名在运行时将参数值放入词法变量中。 因此你可以写

sub foo ($left, $right) : 原型($$) {
返回 $left + $right;
}

原型属性和任何其他属性都在签名之后。

私做 变量 通过 我的()
概要:

我的 $foo; # 声明 $foo 词法本地
我的 (@wid, %get); # 声明局部变量列表
我的 $foo = "flurp"; # 声明 $foo 词法,并初始化它
我的@oof = @bar; # 声明@oof 词法,并初始化它
我的 $x : Foo = $y; # 类似,应用了一个属性

警告:在“我的”声明中使用属性列表仍在不断发展。 目前
语义和界面可能会发生变化。 请参阅属性和 Attribute::Handlers。

“my”运算符声明列出的变量在词法上仅限于封闭的
块,条件(“if/unless/elsif/else”),循环(“for/foreach/while/until/continue”),
子例程、“评估”或“执行/要求/使用”文件。 如果列出了多个值,则
列表必须放在括号中。 所有列出的元素必须是合法的左值。 仅有的
字母数字标识符可能是词法范围的——像 $/ 这样的神奇内置函数目前必须
改为“本地”化为“本地”。

与由“本地”操作符创建的动态变量不同,词法变量声明为
“我的”对外界完全隐藏,包括任何被调用的子程序。 这是
如果它是从自身或其他地方调用的同一个子程序,则为真——每个调用都有自己的
复制。

这并不意味着在静态封闭词法范围中声明的“我的”变量
将是隐形的。 只有动态范围被切断。 例如,“bumpx()”
下面的函数可以访问词法 $x 变量,因为“my”和“sub”
发生在相同的范围内,大概是文件范围。

我的 $x = 10;
子颠簸x { $x++ }

然而,“eval()”可以看到它正在被评估的范围的词法变量,所以
只要名称没有被“eval()”本身的声明隐藏。 参见 perlref。

参数列表到 我的() 如果需要,可以分配给,这允许您初始化
你的变量。 (如果没有为特定变量提供初始值设定项,则创建时使用
未定义的值。)这通常用于命名子程序的输入参数。
例子:

$arg = "弗雷德"; #“全局”变量
$n = 立方根(27);
print "$arg 认为根是 $n\n";
fred 认为根是 3

子立方体根{
我的 $arg = shift; #名字不重要
$arg **= 1/3;
返回$ arg;
}

“我的”只是您可能分配给的东西的修饰符。 所以当你分配给
参数列表中的变量,“my”不会改变这些变量是否被视为
标量或数组。 所以

我的 ($foo) = ; # 错误的?
我的@FOO = ;

两者都向右侧提供列表上下文,而

我的 $foo = ;

提供标量上下文。 但是下面只声明了一个变量:

我的 $foo, $bar = 1; # 错误的

这与效果相同

我的 $foo;
$酒吧 = 1;

直到当前变量之后才引入(不可见)声明的变量
陈述。 因此,

我的 $x = $x;

可用于使用旧 $x 的值初始化新的 $x,以及表达式

我的 $x = 123 和 $x == 123

除非旧的 $x 碰巧具有值 123,否则为假。

控制结构的词法范围不受限定的花括号的精确限制
他们的控制块; 控制表达式也是该范围的一部分。 因此在
循环

而(我的 $line = <>){
$线 = lc $线;
} 继续 {
打印 $line;
}

$line 的范围从它的声明扩展到循环结构的其余部分
(包括“继续”子句),但不能超出它。 类似地,在条件

如果((我的 $answer = ) =~ /^yes$/i) {
用户同意();
} elsif ($answer =~ /^no$/i) {
用户不同意();
} {
咀嚼$答案;
die "'$answer' 既不是 'yes' 也不是 'no'";
}

$answer 的范围从它的声明扩展到该条件的其余部分,
包括任何“elsif”和“else”子句,但不超过它。 参见“简单语句”
perlsyn 有关带修饰符的语句中变量范围的信息。

“foreach”循环默认以以下方式动态确定其索引变量的范围
“当地的”。 但是,如果索引变量以关键字“my”为前缀,或者如果有
在范围内已经是该名称的词法,然后创建一个新的词法。 因此在
循环

对于我的 $i (1, 2, 3) {
一些函数();
}

$i 的范围扩展到循环的末尾,但不超出它,呈现的值
$i 在“some_function()”中无法访问。

一些用户可能希望鼓励使用词法范围的变量。 作为帮助
捕捉隐式使用来打包变量,这些变量总是全局的,如果你说

使用严格的“变量”;

那么从那里到封闭块末尾提到的任何变量都必须引用
到词法变量,通过“我们的”或“使用变量”预先声明,否则必须完全
用包名限定。 否则会导致编译错误。 一个内块
可以用“没有严格的‘变量’”来反驳这一点。

“我的”同时具有编译时和运行时效果。 在编译时,编译器需要
通知它。 这样做的主要用处是安静“使用严格的‘变量’”,但它是
对于 perlref 中详述的闭包的生成也是必不可少的。 实际初始化
但是,它会延迟到运行时间,因此它会在适当的时间执行,例如
例如,每次通过一个循环。

用“my”声明的变量不是任何包的一部分,因此永远不会完全
用包名限定。 特别是,您不得尝试制作
包变量(或其他全局)词法:

我的 $pack::var; # 错误! 非法语法

实际上,动态变量(也称为包或全局变量)仍然是
即使是同名的词法,也可以使用完全限定的“::”符号访问
也可见:

包主;
本地 $x = 10;
我的 $x = 20;
打印 "$x 和 $::x\n";

这将打印出 20 和 10。

您可以在文件的最外层范围声明“我的”变量以隐藏任何此类
来自该文件之外的世界的标识符。 这在精神上类似于 C 的静态
变量在文件级别使用时。 要使用子程序执行此操作需要
使用闭包(访问封闭词法的匿名函数)。 如果你想
创建一个不能从该块外部调用的私有子程序,它可以声明
包含匿名子引用的词法变量:

我的 $secret_version = '1.001-beta';
我的 $secret_sub = sub { 打印 $secret_version };
&$secret_sub();

只要模块内的任何函数从不返回引用,外部就没有
模块可以看到子程序,因为它的名字不在任何包的符号表中。
记住这不是 真的 称为 $some_pack::secret_version 或任何东西; 只是
$secret_version,不合格和不合格。

然而,这不适用于对象方法; 所有对象方法都必须在
要找到的一些包的符号表。 参见 perlref 中的“函数模板”
解决这个问题的方法。

一贯 私做 变量
在 Perl 5.10 中有两种构建持久私有变量的方法。 首先,你可以
只需使用“状态”功能。 或者,如果你想保持兼容,你可以使用闭包
版本早于 5.10。

一贯 变量 通过 状态()

从 Perl 5.10.0 开始,您可以使用“state”关键字代替
“我的”。 但是,要使其正常工作,您必须事先启用该功能,或者通过
使用“功能”编译指示,或在单行上使用“-E”(请参阅​​功能)。 以。。。开始
Perl 5.16,“CORE::state”形式不需要“功能”编译指示。

“state”关键字创建一个词法变量(遵循与“my”相同的范围规则)
从一个子程序调用持续到下一个。 如果状态变量位于
匿名子程序,那么子程序的每个副本都有自己的状态副本
多变的。 但是,状态变量的值仍将在调用之间保持不变
匿名子程序的相同副本。 (不要忘记“sub { ... }”会创建一个新的
每次执行时的子程序。)

例如,下面的代码维护一个私有计数器,每次计数器递增
给我另一个() 函数称为:

使用功能“状态”;
sub gimme_another { 状态 $x; 返回++$x }

这个例子使用匿名子程序来创建单独的计数器:

使用功能“状态”;
子创建计数器{
返回子 { 状态 $x; 返回++$x }
}

此外,由于 $x 是词法的,因此外部的任何 Perl 代码都无法访问或修改它。

当与变量声明结合时,对“状态”变量进行简单的标量赋值(如
in "state $x = 42") 仅第一次执行。 当评估这些语句时
随后的时间,分配被忽略。 这种分配给的行为
非标量变量未定义。

一贯 变量 - 关闭

仅仅因为一个词法变量在词法上(也称为静态)范围是它的
封闭块、“eval”或“do”文件,这并不意味着它在函数内起作用
像一个 C 静态。 它通常更像一个 C 自动,但隐含垃圾
采集。

与 C 或 C++ 中的局部变量不同,Perl 的词法变量不一定得到
回收只是因为他们的范围已经退出。 如果一些更永久的东西仍然知道
的词汇,它会坚持下去。 只要其他东西引用了一个词法,
那个词法不会被释放——它应该是这样。 你不希望记忆被
免费直到你用完它,或者一旦你用完就留在身边。 自动垃圾
收藏会为您解决这个问题。

这意味着您可以传递回或保存对词法变量的引用,而
返回一个指向 C auto 的指针是一个严重的错误。 它还为我们提供了一种模拟 C 语言的方法
函数静力学。 这是一个给函数私有变量的机制
词法范围和静态生命周期。 如果您确实想创建类似 C 的静态内容
变量,只需将整个函数包含在一个额外的块中,并将静态变量放入
在函数之外但在块中。

{
我的 $secret_val = 0;
子给我另一个{
返回++$secret_val;
}
}
# $secret_val 现在外部无法访问
# world,但在调用 gimme_another 之间保留其值

如果这个函数是通过“require”或“use”从一个单独的文件中获取的,那么
这可能就好了。 如果都在主程序中,则需要安排
“我的”要提前执行,或者通过将整个块放在主程序之上,
或者更有可能的是,在它周围放置一个“BEGIN”代码块以确保它
在程序开始运行之前执行:

开始 {
我的 $secret_val = 0;
子给我另一个{
返回++$secret_val;
}
}

有关特殊触发代码,请参阅 perlmod 中的“BEGIN、UNITCHECK、CHECK、INIT 和 END”
块,“BEGIN”,“UNITCHECK”,“CHECK”,“INIT”和“END”。

如果在最外面的范围(文件范围)声明,那么词法的工作方式有点像 C 的
文件静态。 它们可用于在它们下面声明的同一文件中的所有函数,
但无法从该文件外部访问。 这种策略有时会用在模块中
创建整个模块可以看到的私有变量。

临时 理念 通过 当地的()
警告:一般来说,你应该使用“my”而不是“local”,因为它更快更
更安全。 例外情况包括全局标点变量、全局文件句柄
和格式,以及直接操作 Perl 符号表本身。 “本地”主要是
当变量的当前值必须对被调用的子程序可见时使用。

概要:

# 值的本地化

本地 $foo; # 使 $foo 动态本地化
本地(@wid,%get); # 将变量列表设为本地
本地 $foo = "flurp"; # 使 $foo 动态化,并初始化它
本地@oof = @bar; # 使@oof 动态化,并初始化它

本地 $hash{key} = "val"; # 为这个散列条目设置一个本地值
删除本地 $hash{key}; # 删除当前块的这个条目
本地($cond?$v1:$v2); # 支持多种左值
# 本地化

# 符号的本地化

本地*FH; # 本地化 $FH, @FH, %FH, &FH ...
本地 *merlyn = *randal; # 现在 $merlyn 真的是 $randal,加上
# @merlyn 真的是@randal,等等
本地 *merlyn = 'randal'; # 相同的事情:将 'randal' 提升为 *randal
本地 *merlyn = \$randal; # 只是别名 $merlyn,而不是 @merlyn 等

“local”将其列出的变量修改为封闭块的“local”,“eval”,或
“做文件”——并 任何 子程序 被称为 阻止. 一个“本地”只是给
全局(即包)变量的临时值。 它确实 而不去 创建一个本地
多变的。 这称为动态范围。 词法范围是用“my”完成的,它
更像是 C 的自动声明。

某些类型的左值也可以本地化:散列和数组元素和切片,
条件(前提是它们的结果总是可本地化的)和符号引用。
对于简单变量,这会创建新的、动态范围的值。

如果多个变量或表达式被赋予“local”,它们必须放在
括弧。 该运算符的工作原理是将这些变量的当前值保存在其
隐藏堆栈上的参数列表,并在退出块、子例程或
评估。 这意味着被调用的子程序也可以引用局部变量,但不能
全球的。 如果需要,可以将参数列表分配给,这允许您
初始化你的局部变量。 (如果没有为特定变量提供初始值设定项,
它是用未定义的值创建的。)

因为“local”是一个运行时操作符,它每次都通过循环执行。
因此,将变量本地化到循环外更有效。

语法的 注意 on 当地的()

“本地”只是左值表达式的修饰符。 当您分配给“本地”化
变量,“本地”不会改变其列表被视为标量还是数组。
So

本地($foo)= ;
本地@FOO = ;

两者都向右侧提供列表上下文,而

本地 $foo = ;

提供标量上下文。

本地化 of 特别 变量

如果你本地化一个特殊的变量,你会给它一个新的值,但它的魔力
不会消失。 这意味着与此魔法相关的所有副作用仍然适用于
本地化的价值。

此功能允许这样的代码工作:

# 在 $slurp 中读取 FILE 的全部内容
{ 本地 $/ = undef; $slurp = ; }

但是请注意,这会限制某些值的本地化; 例如,
以下语句从 perl 5.10.0 开始失效,并出现错误 修改 of a 唯读
折扣值 尝试,因为 $1 变量是神奇的和只读的:

本地 $1 = 2;

一个例外是默认的标量变量:从 perl 5.14 "local($_)" 开始
始终从 $_ 中去除所有魔法,以便可以在子程序中安全地重用 $_。

警告:绑定数组和散列的本地化目前无法按照描述进行。
这将在 Perl 的未来版本中修复; 同时,避免依赖的代码
本地化绑定数组或散列的任何特定行为(本地化个人
元素还是可以的)。 请参阅 perl58delta 中的“本地化绑定数组和散列被破坏”
,了解更多详情。

本地化 of 球体

构造

本地*名称;

为当前包中的 glob "name" 创建一个全新的符号表条目。 那
意味着其 glob 槽中的所有变量($name、@name、%name、&name 和“name”
文件句柄)被动态重置。

这意味着,除其他外,这些变量最终携带的任何魔法都是
本地丢失。 换句话说,说“local */”不会对内部产生任何影响
输入记录分隔符的值。

本地化 of 分子 of 综合 类型

同样值得花一点时间来解释当您“本地化”一个成员时会发生什么
复合类型(即数组或散列元素)。 在这种情况下,元素是“本地”化的
by 姓名. 这意味着当“local()”的范围结束时,保存的值将是
恢复到其键在“local()”中命名的哈希元素,或数组元素
其索引在“local()”中命名。 如果该元素被删除而“local()”
已生效(例如,通过散列中的“delete()”或数组的“shift()”),它将
弹回存在,可能扩展一个数组并填充跳过的
带有“undef”的元素。 例如,如果你说

%hash = ( 'This' => 'is', 'a' => 'test' );
@ary = (0..5);
{
本地($ary[5])= 6;
本地($hash{'a'}) = 'drill';
而(我的 $e = pop(@ary)){
打印 "$e . . .\n";
最后除非 $e > 3;
}
如果(@ary){
$hash{'only a'} = 'test';
删除 $hash{'a'};
}
}
print join(' ', map { "$_ $hash{$_}" } 排序键 %hash),".\n";
打印 "数组有 ",scalar(@ary)," 元素: ",
join(', ', map { 定义 $_ ? $_ : 'undef' } @ary),"\n";

Perl 会打印

6. . .
4. . .
3. . .
这是一个测试只是一个测试。
该数组有 6 个元素:0, 1, 2, undef, undef, 5

的行为 当地的() 在复合类型的不存在成员上可能会发生变化
的未来。

本地化 缺失 of 分子 of 综合 类型

您可以使用“删除本地 $array[$idx]”和“删除本地 $hash{key}”构造来
删除当前块的复合类型条目并在它结束时恢复它。 他们
返回本地化之前的数组/哈希值,这意味着它们是
分别相当于

做 {
我的 $val = $array[$idx];
本地 $array[$idx];
删除 $array[$idx];
$ val
}



做 {
我的 $val = $hash{key};
本地 $hash{key};
删除 $hash{key};
$ val
}

除了那些“本地”的范围是“做”块。 也接受切片。

我的 %hash = (
a => [ 7, 8, 9 ],
b => 1,
)

{
我的 $a = 删除本地 $hash{a};
# $a 是 [ 7, 8, 9 ]
# %hash 是 (b => 1)

{
我的@nums = 删除本地@$a[0, 2]
# @nums 是 (7, 9)
# $a 是 [ undef, 8 ]

$a[0] = 999; # 当作用域结束时将被擦除
}
# $a 回到 [ 7, 8, 9 ]

}
# %hash 恢复到原来的状态

左值 子程序
可以从子程序返回可修改的值。 要做到这一点,你必须
声明子程序以返回左值。

我的 $val;
子canmod:左值{
$val; # 或:返回 $val;
}
子 nomod {
$val;
}

canmod() = 5; # 赋值给 $val
nomod() = 5; # 错误

子例程和赋值右侧的标量/列表上下文是
就像子程序调用被标量替换一样确定。 例如,考虑:

数据(2,3)=获取数据(3,4);

这里的两个子程序都是在标量上下文中调用的,而在:

(数据(2,3)) = get_data(3,4);

并在:

(data(2)data(3)) = get_data(3,4);

所有子程序都在列表上下文中调用。

左值子程序很方便,但您必须记住,当与
对象,它们可能违反封装。 一个普通的 mutator 可以检查提供的参数
在设置它所保护的属性之前,左值子程序不能。 如果你
在存储和检索值时需要任何特殊处理,请考虑使用
CPAN 模块 Sentinel 或类似的东西。

词法 子程序
警告: 词法子程序仍处于试验阶段。 该功能可能会被修改或
在 Perl 的未来版本中删除。

词法子程序仅在“使用功能‘lexical_subs’”编译指示下可用,
除非“experimental::lexical_subs”警告类别为
禁用。

从 Perl 5.18 开始,您可以使用“my”或“state”声明一个私有子程序。 作为
对于状态变量,“状态”关键字仅在“使用功能'状态'”下可用或
“使用 5.010”或更高版本。

这些子程序仅在声明它们的块内可见,并且仅
在该声明之后:

没有警告“experimental::lexical_subs”;
使用功能“lexical_subs”;

富(); # 调用包/全局子程序
状态子 foo {
富(); # 也调用包子程序
}
富(); # 调用“状态”子
我的 $ref = \&foo; # 引用“状态”子

我的子栏 { ... }
酒吧(); # 调用“我的”子

要从子例程本身内部使用词法子例程,您必须预先声明它。
“sub foo {...}”子例程定义语法尊重任何以前的“my sub;” 或“状态
子;”声明。

我的低音炮; # 预先声明
sub baz { # 定义“我的”子
巴兹(); # 递归调用
}

“状态 子” vs “我的 子”

“国家”潜艇和“我的”潜艇有什么区别? 每次执行
声明“我的”子程序时进入一个块,创建每个子程序的新副本。 “状态”
子例程从包含块的一次执行一直持续到下一次。

所以,一般来说,“状态”子程序更快。 但如果你愿意,“我的”潜艇是必要的
创建闭包:

没有警告“experimental::lexical_subs”;
使用功能“lexical_subs”;

子随便{
我的 $x = 班次;
我的子内{
... 用 $x 做点什么 ...
}
内();
}

在这个例子中,当“whatever”被调用时会创建一个新的 $x,还有一个新的“inner”,
可以看到新的$x。 “状态”子只会看到第一次调用的 $x
“任何”。

“我们的” 子程序

像“our $variable”一样,“our sub”为
一样的名字。

这样做的两个主要用途是切换回使用内部的包 sub
范围:

没有警告“experimental::lexical_subs”;
使用功能“lexical_subs”;

子 foo { ... }

子栏{
我的子 foo { ... }
{
# 这里需要使用外层 foo
我们的子 foo;
富();
}
}

并使子程序对同一范围内的其他包可见:

包 MySneakyModule;

没有警告“experimental::lexical_subs”;
使用功能“lexical_subs”;

我们的子 do_something { ... }

子 do_something_with_caller {
包数据库;
() = 来电者 1; # 设置@DB::args
do_something(@args); # 使用 MySneakyModule::do_something
}

通过 图形符号 参赛作品 (类型团)
警告: 本节描述的机制原本是唯一的模拟方式
在旧版本的 Perl 中传递引用。 虽然它在现代仍然可以正常工作
版本,新的参考机制通常更容易使用。 见下文。

有时您不想将数组的值传递给子程序,而是将名称传递给子程序
它,以便子程序可以修改它的全局副本,而不是使用
本地副本。 在 perl 中,您可以通过前缀来引用特定名称的所有对象
带星号的名称:*foo。 这通常被称为“typeglob”,因为
front 可以被认为是所有有趣的前缀字符的通配符匹配
变量和子程序等。

求值时,typeglob 产生一个标量值,表示所有对象
该名称,包括任何文件句柄、格式或子例程。 当分配给时,它会导致
提到的名称是指分配给它的任何“*”值。 例子:

子双{
本地(*someary)=@_;
foreach $elem (@someary) {
$元素 *= 2;
}
}
双精度(* foo);
双重(*酒吧);

标量已经通过引用传递,因此您可以修改标量参数而无需使用
这种机制通过显式引用 $_[0] 等。您可以修改所有元素
通过将所有元素作为标量传递来创建一个数组,但您必须使用“*”机制(或
等效的引用机制)来“推送”、“弹出”或更改数组的大小。 它
传递typeglob(或引用)肯定会更快。

即使你不想修改一个数组,这个机制对于传递多个
单个 LIST 中的数组,因为通常 LIST 机制会合并所有数组
值,以便您无法提取单个数组。 有关 typeglob 的更多信息,请参阅
perldata 中的“Typeglobs 和文件句柄”。

什么时候 使用 VHDL 语言编写 当地的()
尽管存在“我的”,但仍有三个地方是“本地”运营商
仍然闪耀。 其实在这三个地方,你 必须 使用“本地”而不是“我的”。

1、你需要给一个全局变量一个临时值,尤其是$_。

全局变量,如@ARGV 或标点变量,必须“本地”化
与“本地()”。 这个块读入 /etc/motd, 并将其拆分为分开的块
由等号行,它们放置在@Fields 中。

{
本地@ARGV = ("/etc/motd");
本地 $/ = undef;
本地 $_ = <>;
@Fields = 拆分 /^\s*=+\s*$/;
}

特别是,在分配给它的任何例程中“本地”化 $_ 很重要。
注意“while”条件中的隐式赋值。

2. 需要创建本地文件或目录句柄或本地函数。

需要自己的文件句柄的函数必须在完整的文件句柄上使用“local()”
类型球。 这可用于创建新的符号表条目:

子队列{
本地(*读者,*作家); # 不是我的!
pipe (READER, WRITER) 或 die "pipe: $!";
返回(*读者,*作家);
}
($head, $tail) = ioqueue();

有关创建匿名符号表条目的方法,请参阅符号模块。

因为对 typeglob 的引用赋值会创建一个别名,这可用于
创建有效的本地函数,或者至少是本地别名。

{
本地 *grow = \&shrink; # 直到这个块退出
生长(); # 真正调用shrink()
移动(); # 如果 move() 增长()s,它也会收缩()s
}
生长(); # 再次获得真正的grow()

有关按名称操作函数的更多信息,请参阅 perlref 中的“函数模板”
这条路。

3. 您只想临时更改数组或散列的一个元素。

您可以“本地化”聚合的一个元素。 通常这是在
动力学:

{
本地 $SIG{INT} = '忽略';
函数(); # 不间断
}
# 可中断性在这里自动恢复

但它也适用于词法声明的聚合。

通过 by 型号参考
如果你想将多个数组或散列传递给一个函数——或者从
它——让他们保持完整性,然后你将不得不使用一个明确的
传递引用。 在您这样做之前,您需要了解参考文献中的详细信息
参考文献否则,本节可能对您没有多大意义。

这里有几个简单的例子。 首先,让我们将几个数组传递给一个函数,然后
让它“弹出”所有然后,返回所有以前的最后元素的新列表:

@tailings = popmany (\@a, \@b, \@c, \@d);

子popmany {
我的 $aref;
我的@retlist = ();
foreach $aref (@_) {
推送@retlist,弹出@$aref;
}
返回@retlist;
}

以下是您如何编写一个函数,该函数返回所有出现的键的列表
传递给它的哈希值:

@common = inter(\%foo, \%bar, \%joe);
子间{
我的 ($k, $href, %seen); #当地人
foreach $href (@_) {
while ( $k = 每个 %$href ) {
$看到{$k}++;
}
}
返回 grep { $seen{$_} == @_ } 键 %seen;
}

到目前为止,我们只使用普通的列表返回机制。 如果你想,会发生什么
传递或返回哈希? 好吧,如果你只使用其中之一,或者你不介意它们
连接,然后正常的调用约定是可以的,虽然有点贵。

人们遇到麻烦的地方在这里:

(@a, @b) = 函数(@c, @d);
or
(%a, %b) = 函数(%c, %d);

这种语法根本行不通。 它只设置@a 或%a 并清除@b 或%b。 加上
函数没有被传递到两个单独的数组或散列中:它在@_ 中有一个长列表,
一如既往。

如果能通过引用安排大家处理这个,代码更干净,
虽然不是很好看。 这是一个接受两个数组引用的函数
参数,按照它们有多少元素的顺序返回两个数组元素
他们:

($aref, $bref) = 函数(\@c, \@d);
print "@$aref 比@$bref 多\n";
子功能{
我的 ($cref, $dref) = @_;
如果(@$cref > @$dref){
返回 ($cref, $dref);
} {
返回 ($dref, $cref);
}
}

事实证明,您实际上也可以这样做:

(*a, *b) = 函数(\@c, \@d);
print "@a 比@b\n 多";
子功能{
本地 (*c, *d) = @_;
如果 (@c > @d) {
返回 (\@c, \@d);
} {
返回 (\@d, \@c);
}
}

这里我们使用 typeglobs 来做符号表别名。 不过有点微妙
如果您使用“我的”变量也将不起作用,因为只有全局变量(即使是伪装的
作为“本地”)在符号表中。

如果你传递文件句柄,你通常可以只使用裸类型全局,比如
*STDOUT,但 typeglobs 引用也有效。 例如:

溅(\*标准输出);
子溅射{
我的 $fh = shift;
打印 $fh "她嗯嗯嗯\n";
}

$rec = get_rec(\*STDIN);
子 get_rec {
我的 $fh = shift;
返回标量 <$fh>;
}

如果您打算生成新的文件句柄,您可以这样做。 回传通知
只是裸* FH,而不是它的参考。

子打开{
我的 $path = shift;
本地*FH;
返回 open (FH, $path) ? *FH:未定义;
}

原型
Perl 支持使用函数的一种非常有限的编译时参数检查
原型制作。 这可以在 PROTO 部分或使用原型声明
属性。 如果您声明其中任何一个

子 mypush (+@)
子 mypush :prototype(+@)

然后“mypush()”采用与“push()”完全相同的参数。

如果启用了子程序签名(请参阅“签名”),则更短的 PROTO 语法是
不可用,因为它会与签名冲突。 在这种情况下,原型只能
以属性的形式声明。

函数声明必须在编译时可见。 原型只影响
对函数的新式调用的解释,其中新式被定义为不使用
人物。 换句话说,如果你像内置函数一样调用它,那么它
表现得像一个内置函数。 如果你把它称为老式的子程序,那么
它的行为就像一个老式的子程序。 很自然地从这个规则中得出
原型对像“\&foo”这样的子程序引用或间接引用没有影响
子程序调用,如“&{$subref}”或“$subref->()”。

方法调用也不受原型的影响,因为要调用的函数是
在编译时不确定,因为调用的确切代码取决于继承。

因为这个特性的目的主要是让你定义工作的子程序
像内置函数一样,这里是一些其他几乎解析的函数的原型
与相应的内置完全一样。

声明为称为

子 mylink ($$) mylink $old, $new
子 myvec ($$$) myvec $var, $offset, 1
sub myindex ($$;$) myindex &getstring, "substr"
子 mysyswrite ($$$;$) mysyswrite $buf, 0, length($buf) - $off, $off
子 myreverse (@) myreverse $a, $b, $c
sub myjoin ($@) myjoin ":", $a, $b, $c
子 mypop (+) mypop @array
子 mysplice (+$$@) mysplice @array, 0, 2, @pushme
子 mykeys (+) mykeys %{$hashref}
sub myopen (*;$) myopen 句柄,$name
子 mypipe (**) mypipe READHANDLE, WRITEHANDLE
sub mygrep (&@) mygrep { /foo/ } $a, $b, $c
子 myrand (;$) myrand 42
子我的时间()我的时间

任何反斜线原型字符都代表一个必须以
该字符(可选地以“我的”、“我们的”或“本地”开头),“$”除外,
它将接受任何标量左值表达式,例如“$foo = 7”或
“my_function()->[0]”。 作为@_ 一部分传递的值将是对实际的引用
子程序调用中给出的参数,通过将“\”应用于该参数获得。

您可以使用“\[]”反斜杠组符号来指定多个允许的参数
类型。 例如:

sub myref (\[$@%&*])

将允许调用 我的引用() as

我的引用 $var
我的引用@array
myref %哈希值
myref &sub
myref *glob

和第一个论点 我的引用() 将是对标量、数组、散列、
代码,或 glob。

未反斜杠的原型字符具有特殊含义。 任何未反斜杠的“@”或“%”
吃掉所有剩余的参数,并强制列出上下文。 由“$”表示的参数
强制标量上下文。 “&”需要一个匿名子程序,如果作为
第一个参数,不需要“sub”关键字或后续逗号。

“*”允许子程序接受一个裸字、常量、标量表达式、typeglob、
或对该插槽中的 typeglob 的引用。 该值将可用于子程序
作为一个简单的标量,或者(在后两种情况下)作为对 typeglob 的引用。
如果您希望始终将此类参数转换为 typeglob 引用,请使用
符号::qualify_to_ref() 如下:

使用符号“qualify_to_ref”;

子 foo (*) {
我的 $fh =qualify_to_ref(shift, caller);
...
}

"+" 原型是 "$" 的一个特殊替代,当给定一个
文字数组或散列变量,但会在参数上强制标量上下文。
这对于应该接受文字数组或数组的函数很有用
参考作为参数:

子 mypush (+@) {
我的 $aref = shift;
死“不是数组或数组引用”,除非 ref $aref eq 'ARRAY';
推@$aref, @_;
}

使用“+”原型时,您的函数必须检查参数是否属于
可接受的类型。

分号 (";") 将强制参数与可选参数分开。 它是多余的
在“@”或“%”之前,它们吞噬了其他一切。

作为原型的最后一个字符,或者就在分号、“@”或“%”之前,您可以
使用“_”代替“$”:如果未提供此参数,则将使用 $_ 代替。

请注意解析器如何特别处理上表中的最后三个示例。
“mygrep()”被解析为真正的列表运算符,“myrand()”被解析为真正的一元
一元优先级的运算符与“rand()”相同,而“mytime()”确实没有
参数,就像“时间()”一样。 也就是说,如果你说

我的时间 +2;

你会得到“mytime() + 2”,而不是 我的时间(2), 这就是它的解析方式
原型。 如果要强制一元函数与列表具有相同的优先级
运算符,添加“;” 到原型的结尾:

子 mygetprotobynumber($;);
mygetprotobynumber $a > $b; # 解析为 mygetprotobynumber($a > $b)

“&”的有趣之处在于你可以用它生成新的语法,只要它是
在初始位置:

子尝试 (&@) {
我的($try,$catch) = @_;
评估 { &$try };
如果($@){
本地 $_ = $@;
&$catch;
}
}
子捕捉 (&) { $_[0] }

尝试{
死“胡说八道”;
} 抓住 {
/phooey/ 并打印“unphooey\n”;
};

那打印出“unphooey”。 (是的,仍有未解决的问题与
@_ 的可见性。 我暂时忽略这个问题。 (但请注意,如果我们使
@_ 词法作用域,那些匿名子例程可以像闭包一样......(哎呀,这是
听起来有点Lispish? (没关系。))))

这是 Perl 的“grep”运算符的重新实现:

子 mygrep (&@) {
我的 $code = shift;
我的@result;
foreach $_ (@_) {
push(@result, $_) 如果 &$code;
}
@结果;
}

有些人更喜欢完整的字母数字原型。 字母数字已
为了将来某一天的明确目的,故意将原型排除在外
添加命名的形式参数。 当前机制的主要目标是让模块
编写器为模块用户提供更好的诊断。 拉里感觉这个符号相当
Perl 程序员可以理解,并且它不会对
模块,也不会让它更难阅读。 线路噪声在视觉上被封装成一个
易于吞咽的小药丸。

如果您尝试在原型中使用字母数字序列,您将生成一个可选的
警告 - “原型中的非法字符......”。 不幸的是早期版本的 Perl
允许使用原型,只要其前缀是有效的原型。 警告
可能会在 Perl 的未来版本中升级为致命错误,一旦大多数
违规代码已修复。

最好对新功能进行原型设计,而不是将原型改造为旧功能。
那是因为你必须特别小心不同列表的无声强加
与标量上下文。 例如,如果您决定一个函数只需要一个
参数,像这样:

子功能($){
我的 $n = 班次;
print "你给了我 $n\n";
}

并且有人一直在使用返回列表的数组或表达式调用它:

功能(@foo);
功能(拆分/:/);

然后你刚刚在他们的论点前提供了一个自动的“标量”,这可以是
有点令人惊讶。 曾经持有一件事的旧@foo 没有通过
in. 相反,“func()”现在被传入一个 1; 也就是说,@foo 中的元素数。
并且“拆分”在标量上下文中被调用,因此它开始在您的 @_ 参数上乱写
列表。 哎哟!

如果一个 sub 有一个 PROTO 和一个 BLOCK,原型在 BLOCK 之后才会被应用
是完全定义的。 这意味着具有原型的递归函数必须是
预先声明原型生效,如下所示:

子 foo($$);
子 foo($$) {
富 1, 2;
}

当然,这一切都非常强大,只能适度使用
世界更美好。

常数 功能
具有“()”原型的函数是内联的潜在候选对象。 如果结果
优化和常量折叠后是常量或词法范围的标量
没有其他引用,那么它将被用来代替函数调用
没有 ”&”。 永远不会内联使用“&”进行的调用。 (看 常数.pm 一个简单的方法
声明大多数常量。)

以下函数都将被内联:

sub pi () { 3.14159 } # 不准确,但接近。
sub PI () { 4 * atan2 1, 1 } # 尽善尽美,
# 它也是内联的!
子 ST_DEV () { 0 }
子 ST_INO () { 1 }

sub FLAG_FOO () { 1 << 8 }
sub FLAG_BAR () { 1 << 9 }
sub FLAG_MASK () { FLAG_FOO | 标志栏 }

sub OPT_BAZ () { 不是 (0x1B58 & FLAG_MASK) }

子 N () { int(OPT_BAZ) / 3 }

sub FOO_SET () { 1 if FLAG_MASK & FLAG_FOO }
sub FOO_SET2 () { if (FLAG_MASK & FLAG_FOO) { 1 } }

(请注意,最后一个示例并不总是内联在 Perl 5.20 及更早版本中,
与包含内部作用域的子程序的行为不一致。)您可以撤销
通过使用显式“返回”进行内联:

子baz_val(){
如果(OPT_BAZ){
23返回;
}
其他{
42返回;
}
}
子bonk_val(){返回12345}

如前所述,您还可以在 BEGIN 时动态声明内联子,如果它们
body 由一个没有其他引用的词法范围标量组成。 只有第一个
这里的示例将被内联:

开始 {
我的 $var = 1;
没有严格的“参考”;
*内联 = 子 () { $var };
}

开始 {
我的 $var = 1;
我的 $ref = \$var;
没有严格的“参考”;
*NOT_INLINED = sub () { $var };
}

一个不太明显的警告(参见 [RT #79908])是变量将是
立即内联,并且将不再像正常的词法变量一样运行,例如这将
打印 79907,而不是 79908:

开始 {
我的 $x = 79907;
*RT_79908 = 子() { $x };
$x++;
}
打印 RT_79908(); # 打印 79907

从 Perl 5.22 开始,这种有缺陷的行为虽然为向后兼容而保留,但
检测到并发出弃用警告。 如果您希望子程序被内联(使用
没有警告),确保变量没有在可以修改的上下文中使用
除了它被声明的地方。

# 很好,没有警告
开始 {
我的 $x = 54321;
*内联 = 子 () { $x };
}
#警告。 未来的 Perl 版本将停止内联它。
开始 {
我的 $x;
$ x = 54321;
*ALSO_INLINED = sub () { $x };
}

Perl 5.22 还引入了实验性的“const”属性作为替代。 (禁用
“experimental::const_attr”警告,如果你想使用它。)当应用于
匿名子程序,当“sub”表达式为
评估。 返回值被捕获并变成一个常量子程序:

我的 $x = 54321;
*内联 = 子:const { $x };
$x++;

本例中“INLINED”的返回值将始终为 54321,无论以后如何
对 $x 的修改。 您还可以在 sub 中放置任意代码,它将是
立即执行,其返回值以相同的方式捕获。

如果你真的想要一个带有“()”原型的子程序,它返回一个词法变量,你
可以很容易地通过添加一个明确的“返回”来强制它不被内联:

开始 {
我的 $x = 79907;
*RT_79908 = sub () { 返回 $x };
$x++;
}
打印 RT_79908(); # 打印 79908

判断子例程是否被内联的最简单方法是使用 B::Deparse。 考虑这个
两个子例程返回 1 的示例,其中一个带有“()”原型,使其成为
内联,一个没有(为了清楚起见,deparse 输出被截断):

$ perl -MO=Deparse -le 'sub ONE { 1 } if (ONE) { print ONE if ONE }'
子一{
1;
}
如果一个 ) {
打印 ONE() 如果 ONE ;
}
$ perl -MO=Deparse -le 'sub ONE () { 1 } if (ONE) { print ONE if ONE }'
子一 () { 1 }
做 {
打印1
};

如果您重新定义一个符合内联条件的子程序,您将收到警告
默认。 您可以使用此警告来判断特定子程序是否
被认为是内联的,因为它与覆盖非内联的警告不同
子程序:

$ perl -e '子一(){1}子一(){2}'
常量子程序一在 -e 第 1 行重新定义。
$ perl -we 'sub one {1} sub one {2}'
子程序一在 -e 第 1 行重新定义。

警告被认为足够严重,不会受到影响 -w 开关(或其
缺席),因为先前编译的函数调用仍将使用
函数的旧值。 如果您需要能够重新定义子程序,则需要
确保它不是内联的,或者通过删除“()”原型(改变调用
语义,所以要小心)或以其他方式阻止内联机制,例如通过
添加一个明确的“回报”,如上所述:

sub not_inlined () { 返回 23 }

覆写 内建的 功能
许多内置函数可能会被覆盖,尽管这应该只是偶尔尝试
并且有充分的理由。 通常,这可能是由试图模拟的包完成的
缺少非 Unix 系统上的内置功能。

覆盖只能通过在编译时从模块导入名称来完成——普通
预先声明还不够好。 然而,“use subs”pragma 实际上让你,
通过导入语法预先声明 subs,然后这些名称可能会覆盖内置名称:

使用 subs 'chdir', 'chroot', 'chmod', 'chown';
chdir $某处;
子 chdir { ... }

要明确地引用内置形式,请在内置名称之前加上特殊的
包限定符“CORE::”。 例如,说“CORE::open()”总是指
内置“open()”,即使当前包已经导入了一些其他名为
“&open()”来自别处。 尽管它看起来像一个普通的函数调用,但它不是:
这种情况下的 CORE:: 前缀是 Perl 语法的一部分,适用于任何关键字,
无论 CORE 包中有什么。 参考它,也就是说,
"\&CORE::open",只对部分关键字有效。 见核心。

库模块通常不应该将诸如“open”或“chdir”之类的内置名称作为一部分导出
他们默认的@EXPORT 列表,因为这些可能会潜入其他人的命名空间和
意外改变语义。 相反,如果模块将该名称添加到@EXPORT_OK,
那么用户可以显式导入名称,但不能隐式导入。 那是,
他们可以说

使用模块“打开”;

它将导入“打开”覆盖。 但如果他们说

使用模块;

他们将获得没有覆盖的默认导入。

上述覆盖内置机制的机制非常有意地限制在
请求导入的包。 有第二种方法有时适用
当您希望在任何地方覆盖内置函数时,而不考虑名称空间边界。
这是通过将子导入特殊命名空间“CORE::GLOBAL::”来实现的。 这是
一个非常厚颜无耻地用一些东西替换“glob”操作符的例子
理解正则表达式。

封装 REGlob;
要求出口商;
@ISA = '出口商';
@EXPORT_OK = 'glob';

子进口{
我的 $pkg = 班次;
返回除非@_;
我的 $sym = shift;
我的 $where = ($sym =~ s/^GLOBAL_// ? 'CORE::GLOBAL' : 呼叫者(0));
$pkg->export($where, $sym, @_);
}

子全局{
我的 $pat = shift;
我的@got;
if (opendir my $d, '.') {
@got = grep /$pat/, readdir $d;
关闭 $d;
}
返回@got;
}
1;

这是它可以(ab)使用的方式:

#使用 REGlob 'GLOBAL_glob'; # 覆盖所有命名空间中的 glob()
包 Foo;
使用 REGlob 'glob'; # 仅在 Foo:: 中覆盖 glob()
打印 <^[a-z_]+\.pm\$>; # 显示所有实用模块

最初的评论显示了一个人为的,甚至是危险的例子。 通过覆盖“glob”
在全球范围内,您将强制执行“glob”运算符的新(和颠覆性)行为
每周 命名空间,没有模块的完全认知或合作
拥有这些命名空间。 当然,这应该非常谨慎地进行——如果必须的话
完全完成。

上面的“REGlob”示例没有实现干净覆盖所需的所有支持
perl 的“glob”操作符。 内置的“glob”有不同的行为取决于是否
它出现在标量或列表上下文中,但我们的“REGlob”没有。 确实,很多 perl
内置有这样的上下文敏感的行为,这些必须得到充分的支持
正确编写的覆盖。 有关覆盖“glob”的全功能示例,请学习
标准库中“File::DosGlob”的实现。

当您覆盖内置时,您的替换应该与(如果可能的话)与
内置本机语法。 您可以通过使用合适的原型来实现这一点。 为了得到
可覆盖内置函数的原型,使用带有参数的“原型”函数
“CORE::builtin_name”(参见 perlfunc 中的“prototype”)。

但是请注意,一些内置函数不能用原型来表达它们的语法(例如
“系统”或“咀嚼”)。 如果您覆盖它们,您将无法完全模仿它们
原始语法。

内置的“do”、“require”和“glob”也可以被覆盖,但由于特殊的魔法,
保留了它们的原始语法,您不必为它们定义原型
替代品。 (不过,您不能覆盖“do BLOCK”语法)。

“require”具有特殊的额外黑魔法:如果您调用“require”替换为
“require Foo::Bar”,它实际上会在@_ 中接收参数“Foo/Bar.pm”。 看
perlfunc 中的“需要”。

而且,正如您从前面的示例中注意到的,如果您覆盖“glob”,则“<*>”
glob 运算符也被覆盖。

以类似的方式,覆盖“readline”函数也会覆盖等效的 I/O
操作员 ” ”。另外,覆盖“readpipe”也会覆盖操作符“``”
和“qx//”。

最后,一些内置函数(例如“exists”或“grep”)不能被覆盖。

自动加载
如果你调用一个未定义的子程序,你通常会得到一个直接的、致命的
错误抱怨子程序不存在。 (同样对于正在使用的子程序
作为方法,当该方法不存在于类包的任何基类中时。)
但是,如果在用于定位的一个或多个包中定义了“AUTOLOAD”子例程
原始子例程,然后使用参数调用“AUTOLOAD”子例程
将被传递给原始子程序。 的完全限定名称
原来的子程序神奇地出现在同一个包的全局$AUTOLOAD变量中
作为“自动加载”例程。 该名称不会作为普通参数传递,因为,呃,
嗯,只是因为,这就是为什么。 (作为一个例外,对不存在的“导入”的方法调用
或者只是跳过“unimport”方法。 此外,如果 AUTOLOAD 子例程是
XSUB,还有其他方法可以检索子程序名称。 请参阅“使用 XSUB 自动加载”
在 perlguts 中了解详细信息。)

许多“AUTOLOAD”例程加载到请求子例程的定义中,使用 评估(),
然后使用特殊形式的 去() 擦除堆栈帧
“AUTOLOAD”例程无迹可寻。 (请参阅标准模块的来源
例如,在 AutoLoader 中。)但是“AUTOLOAD”例程也可以只是模拟例程
永远不要定义它。 例如,让我们假设一个未定义的函数
应该只用这些参数调用“系统”。 你要做的就是:

子自动加载{
我的 $program = $AUTOLOAD;
$program =~ s/.*:://;
系统($程序,@_);
}
日期();
我是谁');
ls('-l');

事实上,如果你预先声明你想以这种方式调用的函数,你甚至不需要
括号:

使用 subs qw(date who ls);
日期;
我是谁”;
ls'-l';

一个更完整的例子是 CPAN 上的 Shell 模块,它可以处理未定义的
子程序调用作为对外部程序的调用。

机制可帮助模块编写者将其模块拆分为可自动加载的
文件。 请参阅 AutoLoader 和 AutoSplit 中描述的标准 AutoLoader 模块,
SelfLoader 中的标准 SelfLoader 模块,以及将 C 函数添加到 Perl 的文档
perlxs 中的代码。

子程序 Attributes
子程序声明或定义可能有一个与之关联的属性列表。
如果存在这样的属性列表,它会在空格或冒号边界处被分解,并且
就好像看到了“使用属性”一样。 有关内容的详细信息,请参阅属性
当前支持属性。 与过时的“使用”的限制不同
attrs”,“sub:ATTRLIST”语法用于将属性与预
声明,而不仅仅是子程序定义。

属性必须作为简单的标识符名称有效(没有任何标点符号
比“_”字符)。 他们可能附加了一个参数列表,它只被检查
它的括号 ('(',')') 是否正确嵌套。

有效语法示例(即使属性未知):

sub fnord (&\%) : switch(10,foo(7,3)) : 昂贵的;
sub plugh () : Ugly('\(") :Bad;
子 xyzzy : _5x5 { ... }

无效语法示例:

sub fnord : switch(10,foo(); # ()-string 不平衡
sub snoid : Ugly('('); # ()-string 不平衡
sub xyzzy : 5x5; # "5x5" 不是一个有效的标识符
子插件:Y2::北; # "Y2::north" 不是一个简单的标识符
子 snurt : foo + bar; # "+" 不是冒号或空格

属性列表作为常量字符串列表传递给关联的代码
它们与子程序。 特别是,上面的有效语法的第二个例子
目前就其解析和调用方式而言,如下所示:

使用属性 __PACKAGE__, \&plugh, q[Ugly('\(")], 'Bad';

有关属性列表及其操作的更多详细信息,请参阅属性和
属性::处理程序。

使用 onworks.net 服务在线使用 perlsub



最新的 Linux 和 Windows 在线程序