さて今日は、エラー メッセージの第2回"my" variable %s can't be in a packageについて述べます。


Perlでローカル変数を作るとき、myとlocalの2種類があります。
localはPerl 4の時代からあった古いやり方で、myはPerl 5で追加された新しい(望ましい)やり方です。
myはパッケージに属さないレキシカル変数と言うものを作ります。パッケージは『すぐわかるオブジェクト指向Perl』や『続・初めてのPerl』に出て来る話題で、『かんP』には出てきませんが、大きなプログラムを作るときにpackage文を使って名前空間を切り替えるときに使います。ここではmy変数はレキシカル変数と言ってパッケージには属さない、ということを、ぼんやり理解してください。
use strict;状態で、my宣言しないで変数を使うと「どのpackageの変数か」と聞かれます。『かんP』レベルではmy宣言すればいいのですが、実際にはパッケージ修飾を行えば(レキシカル変数ではなくパッケージ変数を使えば)my宣言しない変数も使えます。
my変数のスコープ(有効範囲)はプログラム ファイル全体、ブロック、またはeval関数の引数の中です。
localも変数を局所化しているように見えますが、実際にはlocalの前までの変数の値をスタックというメモリー領域に覚えておいて、スコープを脱出したらまたスタックの値に戻すという方式でローカル変数を擬似的に発生させています。
myとlocalの違いは、my変数はサブルーチンから見えなくなるが、localは見えるということです。
実行します。
いかがでしょうか。myの方が実感に近いと思います。
なお、上記のプログラムをuse strictを有効化してやってみます。
実行します。いっぱい怒られますけど予定の行動です。
はい、怒られましたね。$sという変数が出ているが、明示的なパッケージ名を書く必要がある、と言われています。
package文によって明示的にパッケージを指定されていない場合、mainというパッケージに入っていることになっています。mainというパッケージに属する$sという変数は$main::sとなります。パッケージ分割しない『かんP』レベルのプログラムでは、プログラムのすべてがmainパッケージに入っています。
ちょっと使ってみます。
こういう研究的なコンテンツはなかなかアホみたいなプログラムが続出しますがお許し下さい。実行します。
OKですね。ここでは$sという変数をmainパッケージで修飾して$main::sという名前で使っています。これでもuse strict;で怒られません。$main::sはmainパッケージに属するパッケージ変数、my $sで宣言した$sはどのパッケージにも属さないレキシカル変数で、赤の他人です。
さて、お待ちかね、今日のエラーメッセージを出現させます。
どうでしょう。
見事にエラーが出ましたね。
「パッケージ変数$main::iを、my宣言している」ということで怒られています。my変数はパッケージ修飾main::をする必要がないので、何か誤解している人のプログラムですね。これはコンパイル エラーで、実行されずに終了します。
ここで、どうしてもブロック内で$main::iをローカル化したいときは、localを使います。
さあどうだ。
無事に出来ましたね。
myではなく、localをどうしても使いたい時は、$/のような特殊変数を一時的に変えたい時です。$/はPerl組み込みのグローバル変数(プログラム全体で共通に使える変数)で、レキシカル変数にはなれませんので、myを使います。
『かんP』では文字列のslurp変換においてlocalを使いました。
詳しい説明は本のp.501にありますが、要は改行コードを格納する特殊変数$/を未定義化することで、ファイル全体を一気に$_に読み込んで置換することで、行をまたがる検索置換を行うものです。上のプログラムの書き方は有名で、slurpといいます。slurpとはずるずるっとすすりこむことだそうです。上のプログラムでは「/*」から「*/」までという、C言語風のコメントを削除しています。
このプログラムで
を
に変えると、下のように怒られます。
グローバル変数$/をmyの中で使ってはいけない、ということで、予定の行動ですね。
ということで
* 基本myを使う
* パッケージ変数やグローバル変数をローカル化するときはlocalを使う
でいきましょう。
localはPerl 4の時代からあった古いやり方で、myはPerl 5で追加された新しい(望ましい)やり方です。
myはパッケージに属さないレキシカル変数と言うものを作ります。パッケージは『すぐわかるオブジェクト指向Perl』や『続・初めてのPerl』に出て来る話題で、『かんP』には出てきませんが、大きなプログラムを作るときにpackage文を使って名前空間を切り替えるときに使います。ここではmy変数はレキシカル変数と言ってパッケージには属さない、ということを、ぼんやり理解してください。
use strict;状態で、my宣言しないで変数を使うと「どのpackageの変数か」と聞かれます。『かんP』レベルではmy宣言すればいいのですが、実際にはパッケージ修飾を行えば(レキシカル変数ではなくパッケージ変数を使えば)my宣言しない変数も使えます。
my変数のスコープ(有効範囲)はプログラム ファイル全体、ブロック、またはeval関数の引数の中です。
localも変数を局所化しているように見えますが、実際にはlocalの前までの変数の値をスタックというメモリー領域に覚えておいて、スコープを脱出したらまたスタックの値に戻すという方式でローカル変数を擬似的に発生させています。
myとlocalの違いは、my変数はサブルーチンから見えなくなるが、localは見えるということです。
#! /usr/local/bin/perl
#
# myLocal.pl -- myとlocalの違い
#use strict; # my宣言もパッケージ修飾もない変数を登場させても怒られないようにstrictを無効化しておく
use warnings;
use 5.010;
$s = 1;
warn "a:", $s; # 1と表示する
{
my $s = 3;
warn "b:", $s; # 3と表示する
&sub_001;
warn "c:", $s; # 3と表示する(ブロックの内側の$sが見える)
}
warn "d:", $s; # 100と表示する(さっきサブルーチンsub_001で変えたから)
{
local $s = 5;
warn "e:", $s; # 5と表示する
&sub_002;
warn "f:", $s; # 200と表示する(さっきサブルーチンsub_002で変えたから。ここもmyと違う)
}
warn "g:", $s; # 100と表示する(localの前に戻る)
sub sub_001 {
warn "s1_a:", $s; # 1と表示する(ブロックの外側の$sが見える)
$s = 100;
warn "s1_b:", $s; # 100と表示する
}
sub sub_002 {
warn "s2_a:", $s; # 5と表示する。ブロックの内側の$sが見える。ここがmyと違う
$s = 200;
warn "s2_a:", $s; # 200と表示する
}
#
# myLocal.pl -- myとlocalの違い
#use strict; # my宣言もパッケージ修飾もない変数を登場させても怒られないようにstrictを無効化しておく
use warnings;
use 5.010;
$s = 1;
warn "a:", $s; # 1と表示する
{
my $s = 3;
warn "b:", $s; # 3と表示する
&sub_001;
warn "c:", $s; # 3と表示する(ブロックの内側の$sが見える)
}
warn "d:", $s; # 100と表示する(さっきサブルーチンsub_001で変えたから)
{
local $s = 5;
warn "e:", $s; # 5と表示する
&sub_002;
warn "f:", $s; # 200と表示する(さっきサブルーチンsub_002で変えたから。ここもmyと違う)
}
warn "g:", $s; # 100と表示する(localの前に戻る)
sub sub_001 {
warn "s1_a:", $s; # 1と表示する(ブロックの外側の$sが見える)
$s = 100;
warn "s1_b:", $s; # 100と表示する
}
sub sub_002 {
warn "s2_a:", $s; # 5と表示する。ブロックの内側の$sが見える。ここがmyと違う
$s = 200;
warn "s2_a:", $s; # 200と表示する
}
実行します。
[sample]$ ./myLocal.pl
a:1 at ./myLocal.pl line 10.
b:3 at ./myLocal.pl line 14.
s1_a:1 at ./myLocal.pl line 31.
s1_b:100 at ./myLocal.pl line 33.
c:3 at ./myLocal.pl line 16.
d:100 at ./myLocal.pl line 19.
e:5 at ./myLocal.pl line 23.
s2_a:5 at ./myLocal.pl line 37.
s2_a:200 at ./myLocal.pl line 39.
f:200 at ./myLocal.pl line 25.
g:100 at ./myLocal.pl line 28.
[sample]$
a:1 at ./myLocal.pl line 10.
b:3 at ./myLocal.pl line 14.
s1_a:1 at ./myLocal.pl line 31.
s1_b:100 at ./myLocal.pl line 33.
c:3 at ./myLocal.pl line 16.
d:100 at ./myLocal.pl line 19.
e:5 at ./myLocal.pl line 23.
s2_a:5 at ./myLocal.pl line 37.
s2_a:200 at ./myLocal.pl line 39.
f:200 at ./myLocal.pl line 25.
g:100 at ./myLocal.pl line 28.
[sample]$
いかがでしょうか。myの方が実感に近いと思います。
なお、上記のプログラムをuse strictを有効化してやってみます。
#! /usr/local/bin/perl
#
# myLocal.pl -- myとlocalの違い
#use strict; # my宣言もパッケージ修飾もない変数を登場させても怒られないようにstrictを無効化しておく
use warnings;
use 5.010;
$s = 1;
warn "a:", $s; # 1と表示する
{
my $s = 3;
warn "b:", $s; # 3と表示する
&sub_001;
warn "c:", $s; # 3と表示する(ブロックの内側の$sが見える)
}
warn "d:", $s; # 100と表示する(さっきサブルーチンsub_001で変えたから)
{
local $s = 5;
warn "e:", $s; # 5と表示する
&sub_002;
warn "f:", $s; # 200と表示する(さっきサブルーチンsub_002で変えたから。ここもmyと違う)
}
warn "g:", $s; # 100と表示する(localの前に戻る)
sub sub_001 {
warn "s1_a:", $s; # 1と表示する(ブロックの外側の$sが見える)
$s = 100;
warn "s1_b:", $s; # 100と表示する
}
sub sub_002 {
warn "s2_a:", $s; # 5と表示する。ブロックの内側の$sが見える。ここがmyと違う
$s = 200;
warn "s2_a:", $s; # 200と表示する
}
#
# myLocal.pl -- myとlocalの違い
#use strict; # my宣言もパッケージ修飾もない変数を登場させても怒られないようにstrictを無効化しておく
use warnings;
use 5.010;
$s = 1;
warn "a:", $s; # 1と表示する
{
my $s = 3;
warn "b:", $s; # 3と表示する
&sub_001;
warn "c:", $s; # 3と表示する(ブロックの内側の$sが見える)
}
warn "d:", $s; # 100と表示する(さっきサブルーチンsub_001で変えたから)
{
local $s = 5;
warn "e:", $s; # 5と表示する
&sub_002;
warn "f:", $s; # 200と表示する(さっきサブルーチンsub_002で変えたから。ここもmyと違う)
}
warn "g:", $s; # 100と表示する(localの前に戻る)
sub sub_001 {
warn "s1_a:", $s; # 1と表示する(ブロックの外側の$sが見える)
$s = 100;
warn "s1_b:", $s; # 100と表示する
}
sub sub_002 {
warn "s2_a:", $s; # 5と表示する。ブロックの内側の$sが見える。ここがmyと違う
$s = 200;
warn "s2_a:", $s; # 200と表示する
}
実行します。いっぱい怒られますけど予定の行動です。
[sample]$ ./myLocal2.pl
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 9.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 10.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 19.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 22.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 23.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 25.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 28.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 31.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 32.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 33.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 37.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 38.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 39.
Execution of ./myLocal2.pl aborted due to compilation errors.
[sample]$
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 9.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 10.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 19.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 22.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 23.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 25.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 28.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 31.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 32.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 33.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 37.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 38.
Global symbol "$s" requires explicit package name at ./myLocal2.pl line 39.
Execution of ./myLocal2.pl aborted due to compilation errors.
[sample]$
はい、怒られましたね。$sという変数が出ているが、明示的なパッケージ名を書く必要がある、と言われています。
package文によって明示的にパッケージを指定されていない場合、mainというパッケージに入っていることになっています。mainというパッケージに属する$sという変数は$main::sとなります。パッケージ分割しない『かんP』レベルのプログラムでは、プログラムのすべてがmainパッケージに入っています。
ちょっと使ってみます。
#! /usr/local/bin/perl
#
# mainPkg.pl -- mainパッケージの実験
use strict;
use warnings;
use 5.010;
$main::i = 3;
say "\$main::iの値の発表(1回目)=>", $main::i;
{
say "\$main::iの値の発表(2回目)=>", $main::i;
}
say "\$main::iの値の発表(3回目)=>", $main::i;
#
# mainPkg.pl -- mainパッケージの実験
use strict;
use warnings;
use 5.010;
$main::i = 3;
say "\$main::iの値の発表(1回目)=>", $main::i;
{
say "\$main::iの値の発表(2回目)=>", $main::i;
}
say "\$main::iの値の発表(3回目)=>", $main::i;
こういう研究的なコンテンツはなかなかアホみたいなプログラムが続出しますがお許し下さい。実行します。
[sample]$ ./mainPkg.pl
$main::iの値の発表(1回目)=>3
$main::iの値の発表(2回目)=>3
$main::iの値の発表(3回目)=>3
$main::iの値の発表(1回目)=>3
$main::iの値の発表(2回目)=>3
$main::iの値の発表(3回目)=>3
OKですね。ここでは$sという変数をmainパッケージで修飾して$main::sという名前で使っています。これでもuse strict;で怒られません。$main::sはmainパッケージに属するパッケージ変数、my $sで宣言した$sはどのパッケージにも属さないレキシカル変数で、赤の他人です。
さて、お待ちかね、今日のエラーメッセージを出現させます。
#! /usr/local/bin/perl
#
# mainMy.pl -- mainパッケージ変数をmy宣言している(エラー)
use strict;
use warnings;
use 5.010;
$main::i = 3;
say "\$main::iの値の発表(1回目)=>", $main::i;
{
my $main::i = 5; # なんだこれ
say "\$main::iの値の発表(2回目)=>", $main::i;
}
say "\$main::iの値の発表(3回目)=>", $main::i;
#
# mainMy.pl -- mainパッケージ変数をmy宣言している(エラー)
use strict;
use warnings;
use 5.010;
$main::i = 3;
say "\$main::iの値の発表(1回目)=>", $main::i;
{
my $main::i = 5; # なんだこれ
say "\$main::iの値の発表(2回目)=>", $main::i;
}
say "\$main::iの値の発表(3回目)=>", $main::i;
どうでしょう。
[sample]$ ./mainMy.pl
"my" variable $main::i can't be in a package at ./mainMy.pl line 14, near "my $main::i"
Execution of ./mainMy.pl aborted due to compilation errors.
[sample]$
"my" variable $main::i can't be in a package at ./mainMy.pl line 14, near "my $main::i"
Execution of ./mainMy.pl aborted due to compilation errors.
[sample]$
見事にエラーが出ましたね。
「パッケージ変数$main::iを、my宣言している」ということで怒られています。my変数はパッケージ修飾main::をする必要がないので、何か誤解している人のプログラムですね。これはコンパイル エラーで、実行されずに終了します。
ここで、どうしてもブロック内で$main::iをローカル化したいときは、localを使います。
#! /usr/local/bin/perl
#
# mainLocal.pl -- mainパッケージ変数をlocal宣言している(合法)
use strict;
use warnings;
use 5.010;
$main::i = 3;
say "\$main::iの値の発表(1回目)=>", $main::i;
{
local $main::i = 5; # へぇー
say "\$main::iの値の発表(2回目)=>", $main::i;
}
say "\$main::iの値の発表(3回目)=>", $main::i;
#
# mainLocal.pl -- mainパッケージ変数をlocal宣言している(合法)
use strict;
use warnings;
use 5.010;
$main::i = 3;
say "\$main::iの値の発表(1回目)=>", $main::i;
{
local $main::i = 5; # へぇー
say "\$main::iの値の発表(2回目)=>", $main::i;
}
say "\$main::iの値の発表(3回目)=>", $main::i;
さあどうだ。
[sample]$ ./mainLocal.pl
$main::iの値の発表(1回目)=>3
$main::iの値の発表(2回目)=>5
$main::iの値の発表(3回目)=>3
[sample]$
$main::iの値の発表(1回目)=>3
$main::iの値の発表(2回目)=>5
$main::iの値の発表(3回目)=>3
[sample]$
無事に出来ましたね。
myではなく、localをどうしても使いたい時は、$/のような特殊変数を一時的に変えたい時です。$/はPerl組み込みのグローバル変数(プログラム全体で共通に使える変数)で、レキシカル変数にはなれませんので、myを使います。
『かんP』では文字列のslurp変換においてlocalを使いました。
#! /usr/bin/perl
#
# slurp.pl -- 複数行にまたがるコメントの削除(一気読み置換)
use 5.010;
use strict;
use warnings;
use utf8;
if ($^O eq "MSWin32") {
binmode STDIN, ":encoding(Shift_JIS)";
binmode STDOUT, ":encoding(Shift_JIS)";
} else {
binmode STDIN, ":encoding(UTF-8)";
binmode STDOUT, ":encoding(UTF-8)";
}
my $infile = shift
or die "usage: $0.txt\n";
my $outfile = $infile;
$outfile =~ s/.txt$/_out.txt/
or die "$infile was not terminated with .txt\n";
open IN, "<", $infile
or die "cannot open $infile as IN because $!\n";
{
local $/;
undef $/;
$_ =;
}
close IN;
open OUT, ">", $outfile
or die "cannot open $outfile as OUT because $!\n";
1 while s{/\*.*?\*/}{}s;
print OUT;
close OUT;
#
# slurp.pl -- 複数行にまたがるコメントの削除(一気読み置換)
use 5.010;
use strict;
use warnings;
use utf8;
if ($^O eq "MSWin32") {
binmode STDIN, ":encoding(Shift_JIS)";
binmode STDOUT, ":encoding(Shift_JIS)";
} else {
binmode STDIN, ":encoding(UTF-8)";
binmode STDOUT, ":encoding(UTF-8)";
}
my $infile = shift
or die "usage: $0
my $outfile = $infile;
$outfile =~ s/.txt$/_out.txt/
or die "$infile was not terminated with .txt\n";
open IN, "<", $infile
or die "cannot open $infile as IN because $!\n";
{
local $/;
undef $/;
$_ =
}
close IN;
open OUT, ">", $outfile
or die "cannot open $outfile as OUT because $!\n";
1 while s{/\*.*?\*/}{}s;
print OUT;
close OUT;
詳しい説明は本のp.501にありますが、要は改行コードを格納する特殊変数$/を未定義化することで、ファイル全体を一気に$_に読み込んで置換することで、行をまたがる検索置換を行うものです。上のプログラムの書き方は有名で、slurpといいます。slurpとはずるずるっとすすりこむことだそうです。上のプログラムでは「/*」から「*/」までという、C言語風のコメントを削除しています。
このプログラムで
local $/;
を
my $/;
に変えると、下のように怒られます。
Can't use global $/ in "my" at C:\Perl\perl\slurp.pl line 31, near "my $/"
グローバル変数$/をmyの中で使ってはいけない、ということで、予定の行動ですね。
ということで
* 基本myを使う
* パッケージ変数やグローバル変数をローカル化するときはlocalを使う
でいきましょう。