┌──────────────────┐
│Perl入門 〜構造化プログラミング編〜 │
│(3)サブルーチン:変数のスコープ │
│2005/10/08 │
└──────────────────┘
[▲Perl Home▲]
●サブルーチンの基礎
◆サブルーチンの記述法
・今回はサブルーチンの使い方を見ていくことにします。例えば下記のコードを見
て下さい。
┌──────────────────────────────────┐
# データ登録
@list = (
'kokubo_34', 'rose_27', 'abe_26', 'kiyohara_22',
'nioka_16', 'shimizu_15', 'takahashi_15', 'nishi_10'
);
# ハッシュ変換
foreach $buf0 (@list) {
@sublist = split(/_/, $buf0);
$sublist[0] =~ tr/a-z/A-Z/;
$hash{$sublist[0]} = $sublist[1];
}
# 結果表示
foreach $buf1 (keys(%hash)) {
print "$buf1 $hash{$buf1} HomeRun!! ";
print 'Great!!' if ($hash{$buf1} > 20);
print "\n";
}
└──────────────────────────────────┘
・上記のコードは明らかに
データ登録 --> ハッシュ変換 --> 結果表示
の3種の部分で構成されています。
・このように簡単なコードならば良いですが、処理が複雑な場合、各部分のコード
は更に長くなるでしょう。例えば全体が100行を超えた場合に、ぱっとコードの
全体構成が見えるでしょうか? もちろん答えは否です。(*1)
・Perlに限らず通常は機能別にサブルーチンを作成しコードを構造化します。まず
は先のコードをサブルーチンを使って構造化してみましょう。
┌──────────────────────────────────┐
#===================================================================
# メイン
&DataInput(); # データ登録
&HashConvert(); # ハッシュ変換
&PrintResult(); # 結果表示
#===================================================================
# データ登録
sub DataInput {
@list = (
'kokubo_34', 'rose_27', 'abe_26', 'kiyohara_22',
'nioka_16', 'shimizu_15', 'takahashi_15', 'nishi_10'
);
}
#===================================================================
# ハッシュ変換
sub HashConvert {
foreach $buf0 (@list) {
@sublist = split(/_/, $buf0);
$sublist[0] =~ tr/a-z/A-Z/;
$hash{$sublist[0]} = $sublist[1];
}
}
#===================================================================
# 結果表示
sub PrintResult {
foreach $buf1 (keys(%hash)) {
print "$buf1 $hash{$buf1} HomeRun!! ";
print 'Great!!' if ($hash{$buf1} > 20);
print "\n";
}
}
└──────────────────────────────────┘
・例を見ればわかると思いますが、サブルーチンは
sub サブルーチン名 { ... 中身 ... }
で記述されます。そして、この関数を呼び出す場合は
&サブルーチン名();
と記述します。
・サブルーチンを使う...構造化コーディングの基本は「分割統治」です。従って
サブルーチンにおいて最低限必要な機能は
+ サブルーチン内で有効な変数(変数の有効範囲に関する理解)
+ 値の受け渡し(パラメータと戻り値)
になります。
●変数の有効範囲(スコープ)
◆変数の有効範囲設定(my関数)
・Perlに限らず、コードを構造化記述するには、変数の有効範囲(スコープ)を理解
することは非常に重要です。Perlに関しては、コード全体(*2)で有効なグローバ
ル変数とブロック内で有効な変数があります。
・例えば、以下のコードを見て下さい。
┌──────────────────────────────────┐
$a = 10;
print "blockの外(1) : $a\n";
{
$a = 5;
print "blockの中 : $a\n";
}
print "blockの外(2) : $a\n";
──────────────────────────────────
[実行結果]
blockの外(1) : 10
blockの中 : 5
blockの外(2) : 5
└──────────────────────────────────┘
・この結果を見ればわかるように、ブロックの中/外に限らず、変数$aは共通のリ
ソースであることがわかります。つまりPerlの場合、変数は原則グローバルの扱
いになります。
・しかし、このままでは非常にコーディングしにくいことは説明するまでもないで
しょう。もし、大きなコードを多人数で作ったら、もしくは他のコードを流用す
ることになったら...大変ですね。
・もちろんPerlにも変数の有効範囲をブロック/サブルーチン内に制限する機能が
あります。それが my 関数です。実際に使用例を見てみましょう。
┌──────────────────────────────────┐
$a = 10;
print "blockの外(1) : $a\n";
{
my $a = 5;
print "blockの中 : $a\n";
}
print "blockの外(2) : $a\n";
──────────────────────────────────
[実行結果]
blockの外(1) : 10
blockの中 : 5
blockの外(2) : 10
└──────────────────────────────────┘
・ブロック { ... } で囲まれた変数 $a を定義(最初に使用)する際に、my を付け
ています。これにより、この$aの有効範囲はブロック内のみとなり、ブロックの
外側で定義された$aとは独立になります。
◆オプション[-w]とプラグマ[use strict;]
・コーディングと変数の関係についてもう少し見ることにしましょう。今まではい
きなり
$a = 10;
のように記述していました。そして
$a = 10;
print ($a + $b), "\n";
と書くこともできますが、これは非常に危険なコードです。それは変数$bの値が
初期化されていないからです。
・このように、変数の有効範囲と初期化があいまいな状態は、構造化コーディング
には向きません。そこで、これらを明確化しなければコンパイラが文句を言うよ
うに設定を行います。それが
+ オプション : -w
+ プラグマ : use strict; (*3)
です。
・先と同様のコードに、このオプションとプラグマを付けて実行すると、エラーメ
ッセージが出るようになります。
┌──────────────────────────────────┐
#!/usr/local/bin/perl -w
#↑この行はperlへのpathとオプション(実行環境により異なります)
use strict;
$a = 10;
$c = $a + $b;
print "c=$c\n";
──────────────────────────────────
[実行結果]
Global symbol "$c" requires explicit package name at testp.pl line 7.
Global symbol "$c" requires explicit package name at testp.pl line 8.
Execution of testp.pl aborted due to compilation errors.
└──────────────────────────────────┘
・エラーメッセージを見ると
「7,8行目の $c には明確なパッケージ名が必要です」
となっています。パッケージという言葉については、グローバル変数と合わせて
後に説明しますが、これは
有効範囲が設定されていないなぁ
--> グローバル or 他のパッケージ変数かな?
--> でも、パッケージ名の宣言も付いてないし...
--> 何だこれ?
のようにコンパイラが解釈した結果のエラーメッセージです。つまり、我々のす
べきことは、「$cの有効範囲を明確化する」ことです。my を付けることにより
変数 $c がコード全体(この場合記述ファイル内)で有効であることを明確化しま
す。
┌──────────────────────────────────┐
#!/usr/local/bin/perl -w
use strict;
my $a = 10;
my $c = $a + $b;
print "c=$c\n";
──────────────────────────────────
[実行結果]
Name "main::b" used only once: possible typo at testp.pl line 7.
Use of uninitialized value in addition (+) at testp.pl line 7.
c=10
└──────────────────────────────────┘
・まだ怒られています。メッセージを見ると
「7行目でmain::b は1回だけ出ています。タイプミス?」
「同じく7行目で、初期化されていない値が演算(+)で使われています」
となっています。そうです。$bの処理も必要です。これも修正しましょう。
┌──────────────────────────────────┐
#!/usr/local/bin/perl -w
use strict;
my $a = 10;
my $b = 20;
my $c = $a + $b;
print "c=$c\n";
──────────────────────────────────
[実行結果]
c=30
└──────────────────────────────────┘
・ようやくメッセージが出なくなりました。これでOKです。このようにスクリプト
のレベルを超えた構造化コーディングを行うには、コードの厳密性にもケアをし
なければなりません。
・さて、今回はサブルーチンの話を始めて、半ば脱線的に変数の話へ移っていきま
した。ですが、サブルーチンの引数と戻り値を扱う場合、この変数の有効範囲に
関して理解していることは、とても重要です。(*4)
・そこで次回はサブルーチン2回目として、引数/戻り値に関する説明をしたいと思
います。
(*1)ある程度個人差はあるのですが、通常はコードの単位として25〜40行程度がMaxと
言われています。感覚としては「1画面で全体が見える」という行数です。
(*2)実際には、グローバル変数、パッケージ変数、ブロック内変数の3種類です。パッ
ケージ(package)については、モジュールのところで説明します。
(*3)プラグマとは、コンパイラへの指令です。use strict; は文字通り「厳密に」。
(*4)Perlはコードの自由度が高いため、変数の有効範囲に関する理解が中途半端でも記
述そのものはできます。10〜20行のスクリプトなら良いですが、数1000行にも及ぶ
そんなコードの解析をさせられた日には、コードの主にちょっとした殺意を抱く状
態になります。せめて我々は殺意を抱かれないように注意したいですね。
[▲Perl Home▲]
Copyright(c)2005 Monpe
All Rights Reserved