サブルーチン
■ サブルーチンを使用する(sub, @_, return)
◆ サブルーチンを定義する
sub を用いてよく使用する処理を サブルーチン として定義しておき、いろいろな箇所から使いまわすことができます。@_ は引数の配列を示します。return はサブルーチンの戻り値を指定します。下記は、2つの引数を受けとって、その合計を返すサブルーチンを定義しています。
# たし算を行うサブルーチン
sub add {
local($x, $y) = @_;
return $x + $y;
}
◆ サブルーチンを呼び出す
サブルーチンを呼び出すには下記の様にします。
$ans1 = &add(3, 5); print "3 + 5 = $ans1\n"; $ans2 = &add(8, 7); print "8 + 7 = $ans2\n";
Perl 4 ではサブルーチンは &add() のように呼び出す必要がありましたが、Perl 5 では & を省略できるようになりました。
$ans1 = add(3, 5); print "3 + 5 = $ans1\n"; $ans2 = add(8, 7); print "8 + 7 = $ans2\n";
実行結果は次のようになります。
3 + 5 = 8 8 + 7 = 15
■ ローカル変数を使用する(local)
◆ うまく動かないサンプル
下記のスクリプトでは、"aaaaa" を表示するサブルーチンを5回呼んでいるので、"aaaaa" が 5行表示されるはずですが、実際には1行しか表示されません。この問題は、変数 $i が グローバル変数(大域変数) として扱われるため、最初に func() が呼ばれた時点で $i の値が 5 となってしまい、最初の for ループも抜けてしまうために発生します。
# === うまく動かないサンプル ===
for ($i = 0; $i < 5; $i++) {
func();
}
# a を 5個表示するサブルーチン
sub func {
for ($i = 0; $i < 5; $i++) { print "a"; }
print "\n";
}
◆ うまく動くサンプル
この問題は、local() を用いて変数 $i を、このサブルーチンの中だけで有効な ローカル変数(局所変数)として定義してやることにより解決できます。
# === うまく動くサンプル ===
for ($i = 0; $i < 5; $i++) {
func();
}
# a を 5個表示するサブルーチン
sub func {
local($i);
for ($i = 0; $i < 5; $i++) { print "a"; }
print "\n";
}
この場合、グローバル変数の $i と func() の中のローカル変数 $i は、名前は同じでもまったく別物の変数として扱われます。
■ ローカル変数を使用する(my)
◆ より隠蔽製の高い my 変数
Perl 5 では local 宣言に加えて、より隠蔽性の高い my が追加されました。下記の例で、func1() の中で参照している $a1 はグローバル変数です。プログラムのどこでも参照できます。$a2 は local() によるローカル変数です。func1() と、そこから呼び出される func2() で有効です。$a3 は my によるローカル変数です。func1() の中でのみ有効です。func2() が参照している $a3 はグローバル変数の $a3 を参照しています。
$a1 = "111"; # グローバル変数($a1)
$a2 = "222"; # グローバル変数($a2)
$a3 = "333"; # グローバル変数($a3)
func1();
sub func1 {
$a1 = "AAA"; # グローバル変数($a1)
local($a2) = "BBB"; # local変数(func1:$a2)
my($a3) = "CCC"; # my変数(func1:$a3)
func2();
print "a1 = $a1\n"; # グローバル変数($a1)
print "a2 = $a2\n"; # local変数(func1:$a2)
print "a3 = $a3\n"; # my変数(func1:$3)
}
sub func2 {
$a1 = "XXX"; # グローバル変数($a1)
$a2 = "YYY"; # local変数(func1:$a2)
$a3 = "ZZZ"; # グローバル変数($a3)
}
実行結果は次のようになります。
a1 = XXX a2 = YYY a3 = CCC
local よりも my の方が隠蔽性が高いので、通常は my、Perl 4 の利用を意識するなら local を使用するのがオススメです。ただし、ファイルハンドルは my 宣言することができません。
■ ファイルハンドルをローカル変数にする
◆ ファイルハンドルをローカル変数として定義する
ファイルハンドルをローカル変数として定義するには、型グロブ を用いて local(*IN); のようにします。local(IN) や my(*IN) を使用することはできません。
@lines = ReadFile("data.txt");
print @lines;
sub ReadFile {
my($file) = @_;
my(@lines);
local(*IN);
open(IN, $file);
@lines = <IN>;
close(IN);
return @lines;
}
■ ファイルハンドルを引数として渡す
◆ ファイルハンドルを引数として渡す
ファイルハンドルの 型グロブ を用いる事により、ファイルハンドルをサブルーチンの引数として渡すことができます。
open(IN, "data.txt");
func(*IN);
close(IN);
sub func {
local(*FILE) = @_;
while (<FILE>) { print; }
}
■ 複数の配列を引数として渡す
◆ うまく動かないサンプル
下記は、A1 A2 B1 B2 と表示されることを期待したコードですが、うまく動きません。配列 @a1 と @a2 が連結されてしまい、func 側で @xx = ( "A", "B", 1, 2 ) 、@yy = ( ) として受け取ってしまうためです。
# === うまく動かないサンプル ===
@a1 = ( "A", "B" );
@a2 = ( 1, 2 );
func(@a1, @a2);
sub func {
local(@xx, @yy) = @_;
local($i, $j);
foreach $i (@xx) {
foreach $j (@yy) {
print "$i$j\n";
}
}
}
◆ うまく動くサンプル
この問題を解決するには 参照渡し というテクニックを用います。サブルーチンに 型グロブ *a1, *a2 を渡してやり、 これを *xx, *yy で受け取ります。こうすることにより、元の配列 @a1、@a2 を @xx、@yy として参照できるようになります。
# === うまく動くサンプル ===
@a1 = ( "A", "B" );
@a2 = ( 1, 2 );
func(*a1, *a2); # @a1, @a2 を *a1, *a2 に変更
sub func {
local(*xx, *yy) = @_; # @xx, @yy を *xx, *yy に変更
local($i, $j);
foreach $i (@xx) {
foreach $j (@yy) {
print "$i$j\n";
}
}
}
Perl 5 では型グロブの代わりにリファレンス(\@a1, \@a2)を渡してやっても同じように動作します。
■ サブルーチンで変数の値を書きかえる(参照渡し)
◆ サブルーチンで変数の値を書きかえる
変数を渡して、サブルーチン側でその値を変更する際にも型グロブやリファレンスを用います。下記の例では、$str1 は値渡し、$str2 は型グロブによる参照渡し、$str3 はリファレンスによる参照渡しを行っています。
$str1 = "Tanaka";
$str2 = "Suzuki";
$str3 = "Yamada";
ToUpper($str1, *str2, \$str3);
print "str1=$str1, str2=$str2, str3=$str3\n";
sub ToUpper {
local($s1, *s2, *s3) = @_;
$s1 = "\U$s1\E"; # 大文字に変換
$s2 = "\U$s2\E"; # 〃
$s3 = "\U$s3\E"; # 〃
}
結果は次のようになります。$str2 と $str3 のみ、サブルーチン内でその値を変更することができ、大文字に変換されています。
str1=Tanaka str2=SUZUKI str3=YAMADA
参照渡しは引数の授受の際に多量のメモリコピーが発生しないため、プログラムの効率を高めることにも役立ちます。
◆ 複数の値を返す
このテクニックを用いて、サブルーチンから複数の値を返却させることもできます。下記の例では、GetSize() サブルーチンにより、高さと幅の 2つの値を取得しています。
GetSize(*height, *width); # 高さと幅を得る
print "height=$height, width=$width\n";
sub GetSize {
local(*hh, *ww) = @_;
$hh = 100;
$ww = 300;
}
■ 引数の省略を可能にする
◆ 引数の省略を可能にする
サブルーチン内で引数の個数を参照し、足りない引数の値を補ってやることにより、引数を省略可能なサブルーチンを実現することができます。下記の例 TimeStr() では、年、月、日、時、分、秒を引数に持ちますが、時、分、秒を省略した場合は 0 を指定したものとみなされます。
print TimeStr(1999, 12, 31, 23, 59, 59);
print TimeStr(2000, 1, 1); # (2000, 1, 1, 0, 0, 0) と解釈される
sub TimeStr {
local($year, $mon, $mday, $hour, $min, $sec) = @_;
# 引数配列の最大インデックス($#_)を参照する
if ($#_ <= 4) { $sec = 0; } # 4以下であれば $sec は 0
if ($#_ <= 3) { $min = 0; } # 3以下であれば $min は 0
if ($#_ <= 2) { $hour = 0; } # 2以下であれば $hour は 0
return sprintf("%04d/%02d/%02d %02d:%02d:%02d\n",
$year, $mon, $mday, $hour, $min, $sec);
}
実行結果は次のようになります。
1999/12/31 23:59:59 2000/01/01 00:00:00
■ コンテキストにより返却値を変える(wantarray)
◆ コンテキストにより返却値を変える
wantarray は、関数の呼び出し側が配列を期待している場合(これを配列の コンテキスト で呼び出しているといいます。)に真になります。これを用いて、スカラーコンテキストと配列コンテキストで返す値を変更することができます。
$scalar = func(); print "scalar = $scalar\n"; # スカラーを期待
@array = func(); print "array = @array\n"; # 配列を期待
sub func() {
# スカラーなら 123 を、配列なら "A", "B", "C" を返却
return (wantarray) ? ( "A", "B", "C") : 123;
}
実行結果は次のようになります。
scalar = 123 array = A B C
■ 戻り値をスカラー値として扱う(scalar)
◆ 戻り値をスカラー値として扱う
scalar() は、スカラーコンテキストで呼ぶことを明示的に指定します。print は通常配列コンテキストで動作しますが、例ではこれを強制的にスカラーコンテキストで扱うようにしています。
print func(); print "\n"; # 配列コンテキストで呼ばれる
print scalar(func()); print "\n"; # スカラーコンテキストで呼び出す
sub func() {
# スカラーなら 123 を、配列なら "A", "B", "C" を返却
return (wantarray) ? ( "A", "B", "C") : 123;
}
結果は次のようになります。
ABC 123
■ 呼び出し元に関する情報を得る(caller)
◆ 呼び出し元に関する情報を得る
caller() は、このサブルーチンを呼び出した元のパッケージ名、ファイル名、行番号を返します。引数 n を指定すると、コールスタックを n 個分さかのぼって情報を得ます。この形式の場合、パッケージ名、ファイル名、行番号に加えて、サブルーチン名、引数を持っているか、結果が配列を期待しているかの情報が付加されます。この機能は主にデバッグの為に用いられます。
func1();
sub func1 {
my($pkg, $file, $line);
($pkg, $file, $line) = caller();
print "pkg=$pkg, file=$file, line=$line\n";
func2();
}
sub func2 {
my (@xx);
@xx = func3();
}
sub func3 {
my($i, $pkg, $file, $line, $sub, $arg, $wa);
$i = 0;
while (($pkg, $file, $line, $sub, $arg, $wa) = caller($i++)) {
print "[$i] $pkg, $file, $line, $sub, $arg, $wa\n";
}
}
結果は次のようになります。
pkg=main, file=subr14.pl, line=1 [1] main, subr14.pl, 14, main::func3, 1, 1 [2] main, subr14.pl, 8, main::func2, 1, [3] main, subr14.pl, 1, main::func1, 1,