要求:给定一个带有前缀列表的文件,每行一个,检查给定的包名称,如果匹配其中一个前缀,则返回true。这是一个项目的子程序,涉及很多包。效率很重要。 O(logn)或O(1)搜索会很棒。
我是Perl的新手。我对此问题进行了一些搜索,并尝试按照the answer in this thread进行操作。我做的唯一更改是从文件中读取前缀。但它不起作用。
这是我的代码:
use strict;
use List::Util qw/first/;
sub isSkippedPackage {
my $packageName = shift;
my $found = first { $packageName =~ /^$_/ } @prefix;
return (defined($found))
}
my $file = qw(packageReplicationBlacklist.cfg);
open my $blacklist, '<', $file;
my @prefix = <$blacklist>;
print "prefix has: ", @prefix;
close $blacklist;
my $skipPackages = 1;
my $test = 'PackageA';
if ( $skipPackages && !isSkippedPackage($test) ) {
print "No prefix for PackageA.\n"
}
$test = 'PackageB';
if ( $skipPackages && isSkippedPackage($test) ) {
print "Got a prefix for PackageB.\n"
}
在packageReplicationBlacklist.cfg文件中:
PackageB
PackageC
目前是:
prefix has: PackageB
PackageC
No prefix for PackageA.
如果我使用“my @prefix = qw / PackageB | PackageC /;”,它会起作用。所以,我的猜测是文件被读入数组而不是一组字符串。如何将其更改为一组字符串?感谢。
答案 0 :(得分:2)
我尝试运行您的程序,并出现以下错误:
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 tutorial中找到。 Perl通过perldoc
命令包含内置文档。您还可以在Perldoc webpage中查看相同的文档。请注意选择了正确版本的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
声明时失去了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;
和use warnings;
语句。你应该得到这样的东西:
Global symbol "$foo" requires explicit package name at ./test2.pl line 10.
这是因为use strict;
和use warnings;
会警告您各种类型的错误。 use strict;
要求您使用our
或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变量那样有sigils。但是,您的文件名是常量。您不希望在程序中间进行更改。 say
。它与print
类似,但您不必在每行末尾继续使用\n
。qq(..)
之间的区别,就像创建带双引号的单词和创建列表的qw(..)
一样。你说my $file = qw(packageReplicationBlacklist.cfg);
在语法上是不正确的。它在这种情况下起作用,因为Perl列表返回了这个特定实例中所有字符串值的标量,所以你很幸运。你想要做的是my $file = qq(packageReplicationBlacklist.cfg);
。事实上,你可能真的只想要q(packageReplacationBlacklist.cfg)
这是真正的单引号。这样,如果文件以@
或$
开头,则不会导致问题。看看Quote like Operators的Perldoc。List::Util
套餐,因为它的工作比它的价值更大。我将向您展示一个稍后使用它的重写子程序。if (defined($blacklist)) {
语句来查看文件是否被打开,我只是取了open
语句的返回值,并使用die
来杀死我的程序如果我无法打开文件。如果你有Perl 5.10.1或更高版本,你也可以使用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 ) ) {
语句将为true。如果找不到$package_name
,first
函数将返回未定义的值,我的if ( is_skipped_package( $package_name, @prefix_list ) ) {
语句将失败。
还有一个问题:这个子程序是一个大型项目的一部分(这就是为什么我没有使用死亡,因为如果没有这样的文件,我们不希望它死掉)。 / p>
足够公平。您可以将整个内容更改为if
语句:if (open my $file, "<", $file) {
。这样,您就可以检查open
是否有效,而不是$file
是否已定义。
如果我想将前缀存储到成员字段(如Java中),请说$ self-&gt; list。怎么做?是pkg = new packages(skipPackages =&gt; 1,list =&gt; @prefix_list);在新方法中,我应该使用@ {$ self-&gt; list}还是$ self-&gt; @list?谢谢!
这开始变得有点复杂......
然而,你问,所以这是一个粗略的样本。该程序将被称为Local / Blacklist.pm。您可以通过说&#34;使用Local :: BlackList&#34;:
来使用它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 是否存在。如果它不存在,我创建一个哈希键&#34; LIST&#34;它只是指向一个匿名数组。在我的list子例程中,我取消引用这个对这个@{$self->{LIST}}
的数组的引用。我可以通过这种方式将数据推送到数组 dereferenced ,我可以返回数组本身。如果我觉得这个阵列可以变得真实,非常大并且是一个记忆猪,我本可以返回对该数组的引用:
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的新变量,还有可怕的 local 变量,它不是&# 39;真的是一个局部变量,但是一个全局包变量,它是Perl在过去二十年中发展起来的一部分。
在99%的情况下,如果使用local $variable
声明变量,则可能是错误的。你知道Adam和Jamie在每个Mythbuster节目之前如何说'不要在家里尝试这个。我们是专业人士?&#34;这是local
声明。不要使用local
,除非你是顶级的Perl开发者,并且喜欢生活在一个可以让你的脸上爆炸的世界。
2. 使用package
语句声明包。一旦使用,所有Perl包变量和函数都在该包中。包主要用于Perl模块,以防止在定义子例程和非词法范围变量时发生名称冲突。有关详细信息,请参阅package功能。
在您的情况下,一切都只是main
包的一部分,这意味着它可以在整个文件中使用,