1

要件: 接頭辞のリストを 1 行に 1 つずつ含むファイルが与えられた場合、指定されたパッケージ名をチェックし、接頭辞の 1 つと一致する場合は true を返します。これは、多くのパッケージを含むプロジェクトのサブルーチンです。だから効率が大事。O(logn) または O(1) 検索は素晴らしいでしょう。

私はPerlが初めてです。この質問について検索を行い、このスレッドの回答をたどろうとしました。私が行った唯一の変更は、ファイルからプレフィックスを読み取ることです。しかし、うまくいきません。

これが私のコードです:

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/;」を使用すると機能します。したがって、ファイルは配列に読み込まれますが、文字列のセットには読み込まれないと思います。それを一連の文字列に変更するにはどうすればよいですか? ありがとう。

4

1 に答える 1

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 チュートリアルにある Perl 変数のスコープについて読む必要があります。Perl には、perldocコマンドによるドキュメントが組み込まれています。Perldoc Web ページでも同じドキュメントを参照できます。正しいバージョンの Perl を選択していることに注意してください。

基本的に、Perl には 2 種類の変数があります。グローバルパッケージ変数とレキシカル スコープのローカル変数です。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;ここで、最後のプログラムを試して、 anduse warnings;ステートメントを有効にします。次のようなものを取得する必要があります。

Global symbol "$foo" requires explicit package name at ./test2.pl line 10.

これは、さまざまな種類のエラーuse strict;use warnings;警告するためです。またはuse strict;を使用して変数を宣言する必要があり、変数がスコープ外になると警告が表示されます。プラグマは、最初に値を指定せずに変数を使用しているという最も重要な警告を大量に表示します。ourmyuse 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プログラムの最初にandを宣言するとmy $skipPackages、プログラムはコンパイルされます。それがあなたが望むことをしないことを除いて。代わりに、以前に発生したエラーが発生します。

より現代的な構文を使用して、プログラムを少し書き直しました。

  • と の両方を使用していuse strict;ますuse warnings;。それはちょうどよいプログラムの練習です。
  • を使用しuse constantて、ファイル名の定数を宣言しました。定数にはPerl 変数のようなシジルがないため、構文は少し奇妙です。ただし、ファイル名は定数です。プログラムの途中でこれを変更したくありません。
  • sayPerl 5.10 以降で利用できるものを使用しています。のようなものですが、各行の最後でprint使い続ける必要はありません。\n
  • qq(..)二重引用符で単語を作成するようなものとリストqw(..)を作成するものとの違いを理解する必要があります。あなた は構文的に間違っていると言いました。この場合、Perl リストはこの特定のインスタンスのすべての文字列値のスカラーを返すため、うまくいきました。あなたがやりたかったことは. 実際、あなたはおそらく真の一重引用符が本当に欲しかっただけでしょう。このように、ファイルがまたはで始まっていても、ファイルは問題を引き起こしません。Quote like Operatorsの Perldoc を見てください。my $file = qw(packageReplicationBlacklist.cfg);my $file = qq(packageReplicationBlacklist.cfg);q(packageReplacationBlacklist.cfg)@$
  • 私は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;
}

私はあなたが本当にやりたかったと思う正規表現ではなく、同等性をチェックしています。関数の値を単に返していることに注意してくださいfirstfirst一致する が見つかった場合$package_name、パッケージ名が返されるため、結果が定義され、私のif ( is_skipped_package( $package_name, @prefix_list ) ) {ステートメントは true になります。$package_nameが見つからない場合、first関数は未定義の値を返し、if ( is_skipped_package( $package_name, @prefix_list ) ) {ステートメントは失敗します。

補遺

もう 1 つの質問: このサブルーチンは大規模なプロジェクトの一部です (そのようなファイルが存在しない場合に終了させたくないため、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 と呼ばれます。「use 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 というクラスを定義しました。かなり単純なクラスです。リストからメンバーを削除する方法はありません。このクラスには 2 つのメソッドが含まれています。1 つはリストにフィールドを追加し、リストを返します。もう 1 つは、メンバーがリストのメンバーであるかどうかを確認します。

新しいクラス オブジェクトを作成するには、次のようにします。

  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);
 }

3 つのサブルーチンがあることに注意してください。これnewは私のコンストラクターです。キーワードnewについて特別なことは何もないことに注意してください。これは、何年にもわたって開発されてきた一種の標準です。私のnewサブルーチンが行ったのは、匿名ハッシュへの参照を作成することだけでした。私が作成するオブジェクトは、このハッシュへの単なる参照です。

私のサブルーチンでは、ハッシュへのキーであるLISTが存在するか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 のショーの前に、Adam と Jamie が「家でこれを試さないでください。私たちはプロなのですか?」と言っているのをご存知でしょう。それがlocal宣言です。localあなたが一流の Perl 開発者であり、物事が目の前で爆発するような世界に住むのが好きでない限り、使用しないでください。

2.パッケージはpackageステートメントで宣言されます。一度使用すると、すべての Perl パッケージ変数と関数がそのパッケージに含まれます。パッケージは主に Perl モジュールで使用され、サブルーチンと非レキシカル スコープの変数を定義する際の名前の衝突を防ぎます。詳細については、パッケージ関数を参照してください。

あなたの場合、すべてがmainパッケージの一部にすぎません。つまり、ファイル全体で利用できます。

于 2012-04-16T04:25:39.607 に答える