┌──────────────────┐
│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