プログラムを実行しようとしましたが、次のエラーが発生しました。
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;
を使用して変数を宣言する必要があり、変数がスコープ外になると警告が表示されます。プラグマは、最初に値を指定せずに変数を使用しているという最も重要な警告を大量に表示します。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
プログラムの最初にandを宣言するとmy $skipPackages
、プログラムはコンパイルされます。それがあなたが望むことをしないことを除いて。代わりに、以前に発生したエラーが発生します。
より現代的な構文を使用して、プログラムを少し書き直しました。
- と の両方を使用してい
use strict;
ますuse warnings;
。それはちょうどよいプログラムの練習です。
- を使用し
use constant
て、ファイル名の定数を宣言しました。定数にはPerl 変数のようなシジルがないため、構文は少し奇妙です。ただし、ファイル名は定数です。プログラムの途中でこれを変更したくありません。
say
Perl 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;
}
私はあなたが本当にやりたかったと思う正規表現ではなく、同等性をチェックしています。関数の値を単に返していることに注意してくださいfirst
。first
一致する が見つかった場合$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
パッケージの一部にすぎません。つまり、ファイル全体で利用できます。