2012-04-16 73 views
1

要求:给定一个带有前缀列表的文件,每行一个,检查给定的包名称,如果与前缀匹配,则返回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. 

它的工作原理,如果我用“我的@prefix = qw/PackageB | PackageC /;“。所以,我的猜测是文件被读入一个数组,但不是一组字符串。我怎样才能把它变成一组字符串?谢谢。

回答

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. 

但是,因为我知道我会得到这些错误是没有让我感到吃惊。我很惊讶你没有得到他们。

您需要阅读Perldocs中的Perlsub tutorial中的Perl变量范围。 Perl通过perldoc命令包含内置文档。您还可以在Perldoc webpage中看到相同的文档。小心你有选择正确版本的Perl。

基本上,在Perl中有两种类型的变量:全局变量包变量和词汇范围局部变量。

包变量使用our $varable;语法定义。词汇范围本地变量使用my $variable;语法定义。

就你而言,你在if statement里声明了一个my @packages变量。这是一个仅在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 

这是因为我们为我们留下的if声明丢失的$foo定义。

一个简单的想法是花括号表示块,并且如果在块内部声明变量为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,它在整个 包中定义 档案

事实上,如果你认为花括号为定义,你能想到的唯一块被看到在块中声明的变量。你甚至可以这样做:

#! /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;要求您声明一个变量为ourmy,并在变量超出范围时向您发出警告。 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 @packagesmy $skipPackages,则程序将进行编译。除了它没有做你想做的事。相反,你会得到你以前的错误。

我用更现代的语法重写你的程序了一下:

  • 我同时使用use strict;use warnings;。这只是一个很好的程序练习。
  • 我已经使用use constant来声明文件名的常量。语法有点奇怪,因为像Perl变量一样,常量没有sigils。但是,您的文件名是一个常量。你不想在程序中间改变它。
  • 我使用的是自Perl 5.10起可用的say。这就像print,但是你不必在每一行的末尾继续使用\n
  • 你需要了解它创建了一个列表qq(..)这就像创建与双引号的词qw(..)之间的差异。你说my $file = qw(packageReplicationBlacklist.cfg);这在语法上是不正确的。它在这种情况下工作,因为Perl列表返回在这个特定的实例中的所有字符串值的标量,所以你幸运了。你想要做的是my $file = qq(packageReplicationBlacklist.cfg);。事实上,你可能真的只想要q(packageReplacationBlacklist.cfg)这是真正的单引号。这样,如果文件以@$开头,则文件不会造成问题。看看Perldoc Quote like Operators
  • 我已经取消了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_namefirst函数返回一个未定义的值,并且我的if (is_skipped_package($package_name, @prefix_list)) {语句将失败。

附录

一个问题:这个函数是一个大项目的一部分(这就是为什么我没有用死,因为我们不希望它死,如果没有这样的文件)。

不够公平。您可以将整个事件更改为if声明:if (open my $file, "<", $file) {。这样,你正在检查是否open工作,而不是$file是否定义。

如果我想将前缀存储到成员字段(比如在Java中),请说$ self> list。怎么做?是像pkg =新软件包(skipPackages => 1,list => @prefix_list);并且在新方法中,我应该使用@ {$ self-> list}还是$ self - > @ list?谢谢!

这开始变得有点复杂......

  • 你需要学习Perl的命名空间有关Perl packages和如何工作的。
  • 您需要了解Perl OOP programming以及它是如何工作的。
  • 您需要了解references。注意在子程序中,我传入整个数组?不好的举止,但我不想进入如何通过对阵列的引用,因为你说你是新来的Perl。
  • 您需要Perl本身更安全的基础。

但是,你问,所以这里是一个粗略的示例。这个程序将被称为Local/Blacklist.pm。你会说“使用本地::黑名单”使用它:

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子程序都创建了一个匿名散列的引用。我创建的对象只是对这个散列的引用。

请注意,在我的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声明变量,则可能是错误的。在每个Mythbuster节目之前,你知道亚当和杰米如何说:“不要在家里试试这个,我们是专业人士?”这是local声明。不要使用local,除非你是一个顶尖的Perl开发人员,并且热爱生活在一个可能会让你的脸上炸毁的世界。

2.一个包声明为package声明。一旦使用,所有Perl包变量和函数都在该包中。在定义子例程和非词法范围变量时,包主要用于Perl模块以防止名称冲突。有关更多信息,请参阅package函数。

在你的情况,一切都只是main包,这意味着它在整个文件中可用的一部分,

+0

谢谢你,大卫,对于非常详细的解释!我已经注意到,我通过声明一个局部变量而在块外部使用它而犯了一个错误。所以,我编辑了原文。 :D – arosima 2012-04-16 05:10:53

+0

哦,我不知道点击进入后会发表评论......无论如何。还有一个问题:这个子程序是大型项目的一部分(这就是为什么我没有使用die,因为如果没有这样的文件,我们不希望它死掉)。如果我想将前缀存储到成员字段(如Java中),请说出$ self> list。怎么做?是否像'pkg = new packages(skipPackages => 1,list => @prefix_list);'在新方法中,我应该使用'@ {$ self-> list}'或'$ self - > @ list '?谢谢! – arosima 2012-04-16 05:24:36

+0

@arosima请参阅我的答案的附录。 Perl可以做到这一点。事实上,这就是Perl 5的全部内容 - 面向对象。但是,这可能比您现在要处理的更复杂。 – 2012-04-16 06:15:58

相关问题