当我阅读Programming Perl,第2版,第51页时,有些东西让我困惑:
sub newopen { my $path = shift; local *FH; #not my! open (FH, $path) || return undef; return *FH; } $fh = newopen('/etc/passwd');
我知道,为什么我们不会重新开始使用我的?到目前为止,如果我们使用my(),我看不出任何问题.
谢谢!
答案很明确,你必须使用local
因为my *FH
是语法错误.
"正确"(但不是很有启发性)的答案是你做错了.您应该使用词法文件句柄和三参数形式open
.
sub newopen { my $path = shift; my $fh; open($fh, '<', $path) or do { warn "Can't read file '$path' [$!]\n"; return; } return $fh; }
要真正回答为什么需要解释词法和全局变量之间以及变量范围和持续时间之间的差异.
变量的范围是程序中名称有效的部分.Scope是一个静态属性.另一方面,变量的持续时间是动态属性.持续时间是程序执行期间变量存在并保存值的时间.
my
声明一个词法变量.词法变量的范围从声明点到封闭块(或文件)的末尾.您可以在不同范围内使用相同名称的其他变量而不会发生冲突.(您也可以在重叠范围中重复使用名称,但不要这样做.)词汇变量的持续时间通过引用计数进行管理.只要存在至少一个对变量的引用,即使该名称在特定范围内无效!my
还有一个运行时效果 - 它分配一个具有给定名称的新变量.
local
有点不同.它适用于全局变量.全局变量具有全局范围(名称在任何地方都有效)以及程序整个生命周期的持续时间.什么local
是临时改变全局变量的值.这有时被称为"动态范围".更改从local
声明点开始并持续到封闭块结束,之后将恢复旧值.重要的是要注意新值不限于块 - 它在任何地方都可见(包括被调用的子例程).引用计数规则仍然适用,因此您可以在更改过期后获取并保留对本地化值的引用.
回到示例:*FH
是一个全局变量.更准确地说,它是一个"typeglob" - 一组全局变量的容器.typeglob包含每个基本变量类型(标量,数组,哈希)的插槽以及一些其他内容.从历史上看,Perl使用typeglobs来存储文件句柄,并且local
使用它们有助于确保它们不会互相破坏.词法变量没有typeglobs,这就是为什么说my *FH
是语法错误.
在Perl的现代版本中,词汇变量可以而且应该用作文件句柄.这让我们回到了"正确"的答案.
在示例代码中,对内置子例程的调用open
使用一个简单的单词作为文件句柄,它相当于一个全局变量.正如Nathan Fellman的回答所解释的那样,local
如果脚本或模块中的其他地方定义了另一个具有相同名称的全局变量,则使用将该单词本地化为当前代码块.这将防止先前定义的全局变量被新声明消除.
这在旧的Perl时代是一种非常常见的做法,但是从Perl 5.6开始,使用标量(使用my
你在问题中暗示的声明)来定义文件句柄要好得多,另外,使用三个参数打电话给open
.
use Carp; open my $error_log, '>>', 'error.log' or croak "Can't open error.log: $OS_ERROR";
顺便说一句,请注意,对于标准输入/输出读写,最好使用两个参数open
:
use Carp; open my $stdin, '<-' or croak "Can't open stdin: $OS_ERROR";
或者,您可以使用该IO::File
模块来祝福该类的文件句柄:
use IO::File; my $error_log = IO::File->new('error.log', '>>') or croak "Can't open error.log: $OS_ERROR");
这里的大部分功劳归功于出色的Perl Best Practices一书的作者Damian Conway.如果您认真对待Perl开发,那么您应该自己购买本书.
你为什么要读一本过时的书.第3版已经出了很长时间了!您使用的是哪个版本的Perl?第2版描述了Perl 5.004(5.4.x)或其左右.
这些天,你不应该使用typeglob表示法来处理文件句柄; 使用'lexical file handles'(参见open,I think)或FileHandle模块,或者其中一个亲戚.
感谢Michael Schwern和Ysth在此收到的评论.