我尝试运行您的程序,但出现以下错误:
Global symbol "@prefix" requires explicit package name at ./test.pl line 8.
Global symbol "$skipPackages" requires explicit package name at ./test.pl line 24.
Global symbol "$skipPackages" requires explicit package name at ./test.pl line 28.
然而,这并不让我感到惊讶,因为我知道我会得到这些错误。我很惊讶你也没有得到它们。
您需要阅读 Perl 变量范围,可以在 Perldocs 的Perlsub 教程中找到。perldoc
Perl 通过命令包含内置文档。您还可以在Perldoc 网页中查看相同的文档。请注意您选择了正确的 Perl 版本。
基本上,Perl 中有两种类型的变量:全局包变量和词法范围的局部变量。1
包变量是使用our $varable;
语法定义的。词法范围的局部变量使用my $variable;
语法定义。
在您的情况下,您my @packages
在if statement
. 这是一个仅在 if 语句本身中可用的变量。试试这个:
#! /usr/bin/env perl
# use strict; #We don't want to use strict!
# use warnings; #Not that either!
if (1 == 1) { #Always true
my $foo = "Foo is defined";
print "1. The value of foo is $foo\n";
}
print "2. The value of foo is $foo\n";
如果我们运行这个程序,我们会得到:
1. The value of foo is Foo is defined
2. The value of foo is
那是因为我们失去了$foo
as 我们离开if
语句的定义。
一个简单的想法是花括号表示块,如果一个变量被声明为my
在块内,它在块外是未定义的。
现在,试试这个:
#! /usr/bin/env perl
# use strict; #We don't want to use strict!
# use warnings; #Not that either!
if (1 == 1) { #Always true
our $foo = "Foo is defined"; #Package Scoped
print "1. The value of foo is $foo\n";
}
print "2. The value of foo is $foo\n";
现在,我们运行这个程序,我们得到:
1. The value of foo is Foo is defined
2. The value of foo is Foo is defined
那是因为当我们用 声明一个变量时our
,它是在整个包 文件中定义的。2
事实上,如果您将花括号视为定义块,您可以认为在块中声明的my变量仅在该块中可见。你甚至可以这样做:
#! /usr/bin/env perl
# use strict; #We don't want to use strict!
# use warnings; #Not that either!
{ #Creating a block...
my $foo = "Foo is defined";
print "1. The value of foo is $foo\n";
} #End of the block
print "2. The value of foo is $foo\n";
同样,你得到:
1. The value of foo is Foo is defined
2. The value of foo is
那是因为花括号表示一个块,一旦你离开这个块,变量就不再被定义了。
现在,尝试最后一个程序并启用use strict;
anduse warnings;
语句。你应该得到这样的东西:
Global symbol "$foo" requires explicit package name at ./test2.pl line 10.
那是因为use strict;
并use warnings;
警告您各种类型的错误。use strict;
要求您使用our
or声明一个变量my
,并在变量超出范围时警告您。use warnings;
编译指示会给你很多警告,最重要的是你在使用变量时没有先给它一个值。
让我们再次重做最后一个程序:
#! /usr/bin/env perl
use strict;
use warnings;
my $foo;
{
$foo = "Foo is defined";
print "1. The value of foo is $foo\n";
}
print "2. The value of foo is $foo\n";
这一次,我my $foo;
在块之外声明,所以它在整个程序中的词法范围。运行这个,我们得到:
The value of foo is Foo is defined
The value of foo is Foo is defined
对于冗长的解释,我很抱歉,但我希望您能更好地理解 Perl 中变量的作用域。如果您在程序的开头声明了my @packages
和my $skipPackages
,则您的程序将编译。除了它不做你想做的事。相反,您会遇到以前遇到的错误。
我使用更现代的语法稍微改写了您的程序:
- 我同时使用
use strict;
和use warnings;
。这只是很好的程序实践。
- 我已经使用
use constant
来为文件名声明一个常量。语法有点奇怪,因为常量不像 Perl 变量那样有符号。但是,您的文件名是一个常量。你不希望这在你的程序中间发生变化。
- 我正在使用
say
自 Perl 5.10 以来可用的。就像print
,但您不必\n
在每行的末尾继续使用。
- 您需要了解
qq(..)
创建带有双引号的单词和qw(..)
创建列表之间的区别。你说 my $file = qw(packageReplicationBlacklist.cfg);
哪个语法不正确。它在这种情况下有效,因为 Perl 列表返回这个特定实例中所有字符串值的标量,所以你很幸运。你想做的是my $file = qq(packageReplicationBlacklist.cfg);
. 事实上,您可能真的只是想要q(packageReplacationBlacklist.cfg)
哪些是真正的单引号。这样,如果您的文件以@
or开头,则不会导致问题$
。查看 Perldoc 中的Quote like Operators。
- 我已经取消了这个
List::Util
包,因为它只是比它的价值更该死的工作。我将向您展示一个稍后使用它的重写子例程。
- 而不是
if (defined($blacklist)) {
查看文件是否打开的语句,我只是获取我的语句的返回值,如果我无法打开文件open
,则使用来杀死我的程序。如果您有 Perl 5.10.1 或更高版本,die
您还可以使用autodie自动为您杀死打开的错误文件。
- 我将我在子程序中使用的所有参数传递给我的子程序。这样,我不依赖于全局变量值。我的子程序使用所有局部变量。
- 最后,我使用一个
foreach
循环来循环我想要测试的所有包。这样,我就不会重复代码。
现在你的程序:
#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
use constant {
FILE_NAME => qq(packageReplicationBlackList.cfg),
};
my @prefix_list;
open my $black_list, "<", FILE_NAME
or die qq(Couldn't open file ") . FILE_NAME . qq(" for reading: $!\n);
chomp ( @prefix_list = <$black_list> );
close $black_list;
foreach my $package_name ( qw(PackageA PackageB) ) {
if ( is_skipped_package( $package_name, @prefix_list ) ) {
say qq(Package "$package_name" has a prefix);
}
else {
say qq(No prefix found for "$package_name");
}
}
sub is_skipped_package {
my $package_name = shift;
my @list = @_;
foreach my $package_to_test (@list) {
if ( $package_name eq $package_to_test ) {
return $package_name;
}
else {
return;
}
}
}
这会产生:
No prefix found for "PackageA"
Package "PackageB" has a prefix
这就是你想要的。
现在,如果您真的想使用 的第一个功能List::Util
,您需要这样做:
sub is_skipped_package {
my $package_name = shift;
my @list = @_;
use List::Util qw(first);
return first { $_ eq $package_name } @list;
}
我正在检查是否相等,而不是我认为您真正想要做的正则表达式。请注意,我只是返回first
函数的值。如果first
找到匹配的$package_name
,它会返回包名,因此结果被定义,我的if ( is_skipped_package( $package_name, @prefix_list ) ) {
陈述将是真的。如果$package_name
未找到,该first
函数将返回一个未定义的值,我的if ( is_skipped_package( $package_name, @prefix_list ) ) {
语句将失败。
附录
还有一个问题:这个子程序是一个大型项目的一部分(这就是我没有使用 die 的原因,因为如果没有这样的文件,我们不希望它死掉)。
很公平。您可以将整个内容更改为if
声明:if (open my $file, "<", $file) {
. 这样,您正在检查是否open
有效,而不是是否$file
已定义。
如果我想将前缀存储到成员字段中(如在 Java 中),请说 $self->list。怎么做?是不是像 pkg = new packages(skipPackages => 1, list => @prefix_list); 在新方法中,我应该使用@{$self->list} 还是$self->@list?谢谢!
这开始变得有点复杂......
- 您需要了解Perl 包以及 Perl 命名空间如何工作。
- 您需要了解Perl OOP 编程及其工作原理。
- 您需要了解参考资料。注意在子程序中,我传入了整个数组?不礼貌,但我不想讨论如何传递对数组的引用,因为你说你是 Perl 的新手。
- Perl 本身需要一个更安全的基础。
但是,您问了,所以这是一个粗略的示例。该程序将被称为 Local/Blacklist.pm。您可以通过说“使用 Local::BlackList”来使用它:
package Local::BlackList;
use strict;
use warnings;
use feature qw(say);
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return;
}
sub list {
my $self = shift;
my $member = shift;
if (not defined $self->{LIST}) {
$self->{LIST} = [];
}
if (defined $member) {
push @{$self->{LIST}}, $member;
}
return @{$self->{LIST}};
}
sub is_member {
my $self = shift;
my $item = shift;
my @list = $self->list;
foreach my $member (@list) {
if ($member eq $item) {
return $item;
}
}
return;
}
我已经定义了一个名为 Local::BlackLlist 的类,它将包含您的列表。这是一个相当简单的类。无法从列表中删除成员。该类包含两种方法:一种将字段添加到列表并返回列表。另一个查看成员是否是列表的成员。
要创建一个新的类对象,您可以这样做:
my $blacklist = Local::BlackList->new;
要将前缀添加到列表中,您可以这样做:
$blacklist->list( $prefix );
要检索列表,您可以这样做:
my @prefix_list = $blacklist->list;
要检查某物是否是列表的成员,您可以这样做:
if ( $blacklist->is_member( $member ) ) {
say qq(Item "$member" is a member of the list);
}
else {
say qq(Item "$member" is not a member of the list);
}
请注意,有三个子例程。这new
是我的构造函数。请注意,关键字new没有什么特别之处。这是多年来发展起来的一种标准。我的new
子程序所做的只是创建对匿名哈希的引用。我创建的对象只是对该哈希的引用。
请注意,在我的list
子例程中,我检查散列的键LIST是否存在。如果它不存在,我创建一个哈希键“LIST”,它只是指向一个匿名数组。在我的列表子例程中,我取消引用这个对数组的引用,如下所示@{$self->{LIST}}
。我可以将东西推送到以这种方式取消引用的数组上,并且我可以返回数组本身。如果我觉得数组可以变得非常非常大并且会占用内存,我本可以返回对数组的引用:
sub list {
my $self = shift;
my $member = shift;
if (not defined $self->{LIST}) {
$self->{LIST} = [];
}
if (defined $member) {
push @{$self->{LIST}}, $member;
}
return $self->{LIST};
}
现在,我必须这样做:
my $list_ref = $blacklist->list;
my @list = @{$list_ref};
将返回的引用转换回数组。顺便说一句,我不喜欢这样,因为它允许人们直接操作数组:
$list_ref = @blacklist->list;
$value= pop @{$list_ref};
这实际上改变了我的班级对象!我想非常小心地将人们的参考资料交还给我的班级结构,因为人们可能会在没有意识到的情况下做某事。
这只是一个体验如何编写面向对象的 Perl。学习基础知识,然后再开始过多地涉及引用和更复杂的数据结构。
1.我撒谎了,现在还有Perl 5.12 新的状态变量,以及可怕的局部变量,它并不是真正的局部变量,而是一个全局包变量,它是 Perl 过去开发的一部分二十年。
在 99% 的情况下,如果您使用 声明一个变量local $variable
,您可能做错了。你知道亚当和杰米在每次流言终结者节目之前是怎么说的“不要在家里尝试这个。我们是专业人士?” 这就是local
宣言。local
除非您是顶级 Perl 开发人员并且喜欢生活在一个事情可能会在您面前爆炸的世界,否则不要使用。
2.用语句声明一个包package
。一旦使用,所有 Perl 包变量和函数都在该包中。包主要用于 Perl 模块中,以防止在定义子例程和非词法范围变量时发生名称冲突。有关详细信息,请参阅包函数。
在您的情况下,一切都只是main
包的一部分,这意味着它在整个文件中都可用,