tjw*_*992 1 variables perl global-variables code-organization scoping
在编写Perl代码时,我经常遇到这样的情况,即我在文件顶部有一个大型变量集群,它们在脚本中充当"全局"变量.我一直在所有国会大写字母中写下这些"全局"变量,以区别于其他人,但我最近一直在研究一些包含许多模块的非常大的脚本,并且非常希望学习一种最小化"全局"使用的好方法变量并使我的代码尽可能安全.
我经常遇到的情况是有两个共享大量变量的子程序:
my $var1, $var2, $var3, $var4, $var5 . . .
sub sub1 {
# uses $var1, $var2, $var3 . . .
# may even make changes to some of these variables
}
sub sub2 {
# also uses $var1, $var2, $var3 . . .
# may change $var1, $var2, $var3
}
sub sub3 {
# doesn't use $var1, $var2, $var3
# still has access to change them
}
Run Code Online (Sandbox Code Playgroud)
将所有这些变量传递到每个子例程中并返回4或5个变量看起来非常难看并且很快就难以跟踪所有变量,但是如果我将这些变量保持全局,那么我遇到了sub3可能编辑它们的问题它不应该.
我知道我可以使用"{}"对范围变量进行操作,但我个人认为这看起来很丑陋:
{
my $var1, $var2, $var3, $var4, $var5 . . .
sub sub1 {
# uses $var1, $var2, $var3 . . .
# may even make changes to some of these variables
}
sub sub2 {
# also uses $var1, $var2, $var3 . . .
# may change $var1, $var2, $var3
}
}
sub sub3 {
# doesn't use $var1, $var2, $var3
# no longer has access to change them
}
Run Code Online (Sandbox Code Playgroud)
另外,如果我使用"{}"来定义变量,如果sub1和sub2共享他们不希望sub3访问的变量会发生什么,然后我也希望sub1和sub3共享sub2无法访问的变量?使用"{}"来定义似乎不可能的变量.尝试这样做看起来像这样:
{
my $varx, $vary, $varz . . .
{
my $var1, $var2, $var3, $var4, $var5 . . .
sub sub1 {
# uses $var1, $var2, $var3 . . .
# may change $var1, $var2, $var3
# uses $varx, $vary, $varz . . .
# may change $varx, $vary, $varz
}
sub sub2 {
# also uses $var1, $var2, $var3 . . .
# may change $var1, $var2, $var3
# does not use $varx, $vary, $varz . . .
############################################
# still able to change $varx, $vary, $varz #
############################################
}
}
sub sub3 {
# doesn't use $var1, $var2, $var3
# no longer has access to change them
# uses $varx, $vary, $varz . . .
# may change $varx, $vary, $varz
}
}
Run Code Online (Sandbox Code Playgroud)
除了使用"{}"之外,还有更好的方法来限制访问吗?即使唯一的解决方案是进入面向对象的Perl,也可以随意抛出一些想法(如果你进入面向对象的Perl,请使用非常具有描述性的,我仍然对使用面向对象编程有些新意).
谢谢!
在评论和Sobrique的回答中,为什么你不应该使用全局变量.我想给出一个简单的(人为的)示例,说明如何通过使用对象来避免全局变量:
Knight.pm
package Knight;
use Moose;
use namespace::autoclean;
has 'name' => (
is => 'rw',
isa => 'Str',
required => 1
);
has 'quest' => (
is => 'rw',
isa => 'Str',
required => 1
);
has 'favorite_color' => (
is => 'rw',
isa => 'Str',
required => 1
);
__PACKAGE__->meta->make_immutable;
Run Code Online (Sandbox Code Playgroud)
cross_bridge_of_death
use strict;
use warnings FATAL => 'all';
use 5.010;
use Knight;
use List::Util qw(none);
sub cross_bridge {
my ($crosser) = @_;
my @answers = ($crosser->name, $crosser->quest, $crosser->favorite_color);
my $success = none { /\. No / } @answers; # Can't change your answer
return $success;
}
my $lancelot = Knight->new(
name => 'Sir Lancelot of Camelot',
quest => 'To seek the Holy Grail',
favorite_color => 'Blue'
);
my $galahad = Knight->new(
name => 'Sir Galahad of Camelot',
quest => 'To seek the Holy Grail',
favorite_color => 'Blue. No yel...'
);
foreach my $knight ($lancelot, $galahad) {
say $knight->name, ': "Auuuuuuuugh!"' unless cross_bridge $knight;
}
Run Code Online (Sandbox Code Playgroud)
Sir Galahad of Camelot: "Auuuuuuuugh!"
Run Code Online (Sandbox Code Playgroud)
此示例使用Moose,它只是改进Perl本机OO语法的几个模块之一.
现在,这可能看起来像很多代码只是检查一些字符串.毕竟,我们可以Knight完全摆脱这个类并改变我们的cross_bridge功能,以便我们可以这样称呼它:
cross_bridge($name, $quest, $favorite_color);
Run Code Online (Sandbox Code Playgroud)
甚至:
cross_bridge({
name => $name,
quest => $quest,
favorite_color => $favorite_color
});
Run Code Online (Sandbox Code Playgroud)
但那时我们必须跟踪三个变量而不是一个变量.使用对象允许我们通过单个变量访问多个属性,因此您可以减少传递给子例程的参数数量.
面向对象是一个很大的主题(我甚至没有涉及方法,它可以更简化你的代码).我建议阅读perlootut然后浏览Moose手册,从Moose概念部分开始.还有其他一些受欢迎的Moose替代品perlootut.