|
||||
|
|
9章 サブルーチン、ライブラリ
|
|
|
サブルーチンとは
同じ処理が複数回出てくる場合を考えてみてください。例えば一箇所に修正が入ったとしても複数あるロジックの全てを修正しなくてはいけません。そのようなプログラムは信頼性も低くなりますし、作業効率も当然悪くなります。このようなときはサブルーチンと呼ぶ機能を用い処理単位で独立させて記述する事が出来ます。独立させて記述しておくので何度も同じ記述をする必要がなくコーディング量を減らす事も出来ます。 またバグが見つかった際の改修も、この一箇所のみで済んでしまうためメンテナンスがとても容易になります
※※C言語, Java, Visual Basic 等で関数と呼ばれる機能( 若干機能的に異なる部分もあります )
宣言 & 呼び出し方宣言
サブルーチンの定義は次のように行います。サブルーチン名、引数のリストを指定しますが引数のリストは無くても構いません。 このうようにサブルーチンで宣言した箇所については、明示的にこのルーチンを呼び出さない限り実行されません。
sub サブルーチン名 { 処理 } 呼び出し
上記でも説明しましたが、にサブルーチンを宣言しただけではサブルーチンは実行されません。 プログラマによって明示的に呼び出す事によって始めて実行されます。サブルーチンを指定する場合にはプレフィクス [ & ] を付けます。 プレフィクスを用いた呼出し
# 引数を与えない呼び出し &サブルーチン名 ; # 引数を与える呼び出し &サブルーチン名 ( 引数リスト ) ; doを用いた呼び出し
# 引数を与えない呼び出し &do &サブルーチン名 ; # 引数を与える呼び出し &do &サブルーチン名 ( 引数リスト ) ; ※名前に曖昧さが無い場合はプレフィクスを省略する事ができます。ただし、defined() や undef() の引数として使う場合のように、サブルーティンを名指しするときには [ & ] を省略する事は出来ません。
# サブルーチンの定義(この時点では実行されません) sub syori_A { print "A" ; } # サブルーチンの定義(この時点では実行されません) sub syori_B { print "B" ; } # プレフィクス省略可能 &syori_A ; # プレフィクス省略可能 &syori_B ; do &syori_B ; do &syori_A ; # 表示 → ( ABBA ) &do &サブルーチン名 ; 値の戻し、引数の受け取り結果の戻し
return [ return ] ステートメントにより 引数に指定された値をサブルーチンの結果として呼び出し側に知らせることが出来きます。 [ return ] ステートメントは実行された時点でサブルーチンを終了して呼び出し側に制御が戻る。 [ サンプル 1 ]
print &sub_A; sub sub_A { return "Hello World\n" ; } [ 出力 ]
Hello World
[ サンプル 2 ]
&sub_A ; sub sub_A { print "This is" ; return; print "Sample" ; } [ 出力 ]
This is
return を用いない場合 サブルーチン内で [ return ] ステートメントを実行しなければ、サブルーチンの最後まで処理が順次実行されます。 その際 最後に評価された値をサブルーチンの戻り値として戻すことになります。 [ サンプル ]
if ( &sub_A ) { print "数値が含まれています\n" ; } else { print "数値は含まれていません\n" ; } sub sub_A { $x = "Hello World!!" ; $x =~ /[0-9]/ ? 1 : 0 ; } 上の例では関数の戻り値を if 分の判定条件として使用しています。 サブルーチン内では [ return ] ステートメントは使用していないので最後に実行された正規表現による結果が返されます。 引数の受け取り
C言語 や Visual Basic と同じで、サブルーチンへ引数を指定してサブルーチンの処理を変更することが出来ます。 指定した引数は特殊配列の [ @_ ] へ格納されます。
&Sub_A ( "Hello" , "World" , "!!" ) ;
[ 引数リスト ]
[ 受け取り例 ]
sub Sub_A { ( $a , $b , $c ) = @_ ; # 特殊変数からの引取り } 変数のスコープ
Perlで通常の変数は名前空間の中では全てグローバルなスコープを持つことにななっています。 名前空間とは package句 を記述することにより変更できるもので、何も指定していない場合はデフォルトで名前空間 [ main ] が使用されています。
つまり明示的に変更しない限りは全て同じ名前空間 [ main ] を使ったグローバル変数となってしまいます。これは小規模なスクリプトの作成には支障無いと思いますが、大きなスクリプトになってくると変数の重複があり予期せぬ結果になってしまう事があります。 package packageを使用するとファイルの終了、又は次に package が現れるまで引数で指定した名前空間が使用されます。これにより多数の変数がある場合に変数の重複を防ぐことが出来きます。別空間の変数へアクセスする場合は [ $空間名'識別子名 ] と記述します。主にライブラリ(後述)の先頭などに付けられ変数の重複を防ぐことが多いです。 [ 表記法 ]
package 名前空間名 ;
[ サンプル ]
# 名前空間 main を使用( デフォルト ) $a = 10 ; # 名前空間 A の開始 package A ; $a = 100 ; # 名前空間 main に戻す package main ; # main の a を表示 → 10 print " a = $a \n " ; # 名前空間 A に戻す package A ; # A の a を表示 → 100 print " a = $a \n " ; # 名前空間 C の開始 package C ; # main の a を表示 → 10 print " a = $main'a \n " ; # A の a を表示 → 100 print " a = $A'a \n " ; local サブルーチンやブロックの内部で使用します。 localを使用すると、その時点の識別子が同じグローバル変数の値を保存します。そしてブロックが終わると保存した値をスタックから戻します。 この変数は一時的にはグローバルな変数を持つことになりますが前の値を復帰させる為にサブルーチン内でのローカル変数として使用します。 [ 表記法 ]
local 変数名 ;
[ サンプル ]
# グローバル変数への値代入 $a = 100 ; # グローバル変数の表示 ( 100 ) print $a ; { # ローカル変数の宣言、代入(グローバル変数の待避) local $a = 1 ; # ローカル変数の表示 ( 1 ) print $a ; } my [ レキシカル変数 ] サブルーチンやブロックの内部で使用します。 [ 2章 ] で述べてあるシンボルテーブルには登録されず、サブルーチンやブロック等が保持する変数集合の空間(スクラッチパッド)が割当てられます。localとの違いは完全なローカルな変数となり他のサブルーチンからはアクセスする事が出来ないことです。 またシンボルテーブルへ登録されないので、当然 型グロブも存在しません。 [ 表記法 ]
my 変数名 ;
[ サンプル ]
# グローバル変数への値代入 $a = 100 ; # グローバル変数の表示 ( 100 ) print $a ; { # レキシカル変数の宣言、代入 my $a = 1 ; # レキシカル変数の表示 ( 1 ) print $a ; } local と my の異なる点 local と my は一見動作が似ているように見えますが若干異なる点があります。 大きく異なる点として、my ( レキシカル変数 ) の方が高速であるという事です。 また localではグローバル変数を一時的に待避をしているだけなので一時的にはグローバルなスコープを持ちますた、 myで宣言した レキシカル変数の場合は完全なローカル変数として扱われます。 [ サンプル ]
$a = 100 ; $b = 200 ; { local $a = 1 ; # @ my $b = 2 ; # A print $main'a ; # B print $main'b ; # C } 上記 @ の local で宣言された時点で $a は名前空間 ( main ) のグローバルとなっている為に B 時点では "1" と表示されます。 A の my で宣言されたレキシカル変数はシンボルテーブルへは登録されず完全なローカル変数として扱われるので C の名前空間( main )の変数へのアクセスは "200" が表示されます。 my( レキシカル変数 )へは呼出し側のサブルーチンからもアクセスする事は出来ません。( 下記参照 )
# グローバル変数の定義、代入 $a = 100 ; $b = 200 ; &sub_A ; sub sub_A { # レキシカル宣言&初期化 my $a = 1 ; # ローカル宣言&初期化 local $b = 2 ; # サブルーチン呼出し &sub_B ; } sub sub_B { # 表示 → ( 100 ) print $a ; # 表示 → ( 2 ) print $b ; } 参照渡し
[ 変数のスコープ ] のように package, local, myを使用する事によりサブルーチンをブラックボックスとして扱う事が出来ます。しかし、return を使用した場合でも、使用しなかった場合でも、呼び出し側で戻り値として取得できる値は一つだけです。 サブルーチンでの結果を複数使用したというケースは多々あると思いますが、そんな時は 参照渡し、型グロブ、リファレンス といったテクニックを使うことでサブルーチンから複数の値を呼び出し元へ返すことが出来ます。
参照渡しによる方法 Perl はサブルーチンへの引数がデフォルトで参照渡しとなっているため、この参照渡しを利用することで、サブルーチンから結果を複数戻すことが出来るようになります。 ※ C言語のポインタ、Visual Basoc の ByRefによる引数受け取りに似ています
$a = 100 ; # 関数の呼び出し &sub_A ( $a ); # 表示 → ( 10 ) print $a ; sub sub_A{ # 引数の更新 $_[0] = 10 ; } 上記のようにサブルーチンの引数を変更した値が呼び出し元でも変更されているのが確認できます。 型グロブによる方法 型グロブ自体を print を使い出力すると次のような結果が得られます。
print *a ; # 表示 → ( *main::a )
引数に型グロブを指定することにより異なる名前空間でもサブルーチン内の local 変数でも引数に与えられた変数と同一の変数を指し示すことが可能となります。
$a = 100 ; # 型グロブの渡し &sub_A ( *a ); # 表示 → ( 10 ) print $a ; sub sub_A{ # 引数の取得 local *b = $_[0] ; # 変数の更新 $b = 10 ; } 今回は引数で *a を指定しサブルーチン側ではこれを *b で受け取っています。型グロブを指定することによって変数 $b へ変更を加えると $a へも変更が反映されます。 リファレンスによる方法 Perlでは確保されているメモリ領域を参照する為に [ リファレンス ] というものを使用します。既存の変数のリファレンスを取得するにはそれに、バックスラッシュ、Windowsであれば [ \ ] を付ける事により取得する事が出来ます。
$a = 10 ; # $aへのリファレンス $raを作成 $ra = \$a ; 受け取り側(デリファレンス) [ リファレンスによる方法 ] で代入した変数 $ra を printすると "SCALAR(0x655ecc)"このような訳の分らない結果が得られます。( 環境により全く同じにはなりません ) これを $a の代わりに使用する場合は次のように デリファレンス と言う操作を行います。
$a = 10 ; # $aへのリファレンス $raを作成 $ra = \$a ; # print $a ; と同じ(参照元の値にアクセスする) print $$ra ; 上記のように 通常のプレフィクス $ra の前に [ $ ] をもう一つ付ける事によりデリファレンスを行うことが出来ます。 [ 使用例 ]
$a = 100 ; # 参照渡し &sub_A ( \$a ) ; # サブルーチンでの変更が反映されている print $a ; sub sub_A { # 参照渡しの受け取り my $b = $_[0] ; # デリファレンスして代入($aへの代入と同じ) $$b = 10 ; } 型グロブを用いた場合はレキシカル変数 ( my )では型グロブを取得出来ませんがリファレンスを引数として渡すことでレキシカル変数 ( my )で受け取ることが可能です。 ライブラリ
スクリプト内のサブルーチンの他に、他人の作成したファイル内にあるサブルーチンを利用する時は 、環境変数PERLLIBで指定されたフォルダからカレントディレクトリと指定されたライブラリが検索されていきます。
ライブラリ作成方法 [ 例 Comment.pl ]
package Comment ; # @ sub Set_Comment { *Array = $_[0] ; my @Ret ; foreach $Line ( @Array ) { push @Ret , ( '#' . $Line ) ; } @Array = @Ret ; } 1 ; # A 先ず @ でライブラリで用いる名前空間名を宣言します。これを宣言していないと利用者側で変数の重複が無いかのチェックをしなくてはいけないので付けるようにしましょう。 ※慣習としてライブラリ名 ( スクリプト名 ) を付ける事が多い ライブラリが正常に読み込めた事を表わすようにスクリプトファイルの末尾に付けておきます ライブラリ使用方法 型グロブ自体を print を使い出力すると次のような結果が得られます。
require 'Comment.pl' ; # @ @Arr = ( "This", "is", "sample" ) ; &Arr_print ( *Arr ) ; # A &Comment::Set_Comment( *Arr ) ; # B &Arr_print( *Arr ) ; @ ライブラリの使用を宣言する(必須) A モジュール内サブルーチン(配列を1行ずつ出力 ※記載省略) B ライブラリ内のサブルーチン呼出し [ モジュール使用前の出力 ]
This is sample [ モジュール使用後の出力 ]
#This #is #sample 文字コード変換ライブラリ( jcode.pl )
掲示板プログラムなどのスクリプトを作成する場合は、【複数のプラットフォームからの入力を受け付ける】という事を考えなくてはいけません。さらにプラットフォームによって異なった文字コードを使用していると言う事を知っておく事が必要です。入力データそのままをファイルに保存した場合に、一つのファイルに複数の文字コードが混在する状態になってしまい、不具合が生じます。そこで次のような文字コード変換ライブラリを使用して保存する文字コードを統一するといった手法が広く使用されています。例は jcode.pl です。 コード変換法
&jcode::convert ( *string , '文字コード名' ) ;
[ 文字コードと設定値 ]
第一引数に型グロブを指定しこの変数に対して変換が行われる事になります。 第二引数に指定できる文字コードを記述します。変換されるコードは上の通りです。 コード判別法
$Ret = &jcode::getcode( *string ) ;
第一引数に指定した変数の文字コードが戻り値として変数 $Ret に返されます。 ※ ライブラリは Perl4 の時に使用されていた方法になります。 Perl5 からは [ オブジェクト指向 ] をサポートしており、ライブラリではなくモジュール [ 拡張子が .pm ] を使用する事が推奨されています。 今回説明した jcode.pl も、モジュール版として Jcode.pm というものが配布されています。 |
||||||||||||||||||