Hatena::ブログ(Diary)

ちゃまぐの備忘録

2011-03-28

[] 続・はじめてのPerl 10章

10章 大規模なプログラムの構築


10.2 evalによるコードの挿入
# Example.pm
sub println
{
    my $message = shift;
    print "$message\n";
}
# sample.pl
sub load_common_subroutin
{
    my $pm_file = shift;

    open my $code_fh, $pm_file or die $!;

    undef $/;
    my $code = <$code_fh>;

    close $code_fh;

    eval $code;

    # $code内に構文エラーが含まれている場合、
    # $@に適切なエラーメッセージが格納される
    die $@ if $@;
}

load_common_subroutin( 'Example.pm' );

println( "Hello world!" );

10.3 doを使う方法

10.2 のsample.plはdoを使うことで、以下のようにかける

do 'Example.pm';
die $@ if $@;

println( "Hello world!" );
do演算子
    現在のプログラムに、Example.pmのコードをインクルードするように動作する
        インクルードされたファイル内のレキシカル変数とuse strictなどのディレクティブは、
        メインプログラムに影響を与えない

10.4 requireを使う方法

以下のように、Example.pmをインクルードするExample2.pmが定義する

# Example2.pm
do 'Example.pm';
die $@ if $@;

sub printlist
{
    my @list = @_;

    for ( @list ) {
        println( $_ );
    }
}

そして、sample.plを以下のように修正する

# sample.pl
do 'Example.pm';
die $@ if $@;

do 'Example2.pm';
die $@ if $@;

println( "■ コーヒー豆一覧" );

my @list = qw/
    アメリカンブレンド
    ブルマンブレンド
    マイルドブレンド
    モカブレンド
/;

printlist( @list );

sample.plを実行すると、Perlは、println()を2回定義してしまう。

警告を有効にしている場合、サブルーチンの再定義を行ったと警告を受けてしまう。

$ perl sample.pl
Subroutine println redefined at Example.pm line 3.
■ コーヒー豆一覧
アメリカンブレンド
ブルマンブレンド
マイルドブレンド
モカブレンド
ファイルをインクルードしたのか、してないのか管理する仕組みが必要
    Perlでは、require演算子でその仕組みを提供する

require演算子
    Perlga読み出したファイルを%INCを使い管理する
        一度ファイルの処理に成功すると、同じファイルに対するrequire処理を無視する

    読み込むファイルに構文エラーが含まれている場合、プログラムは異常終了する
        do演算子を津型場合の、"die $@ if $@;"が不要になる

    ファイル内で最後に評価される式は、真の値を返さなければならない
        これが、requireで評価されるファイルのコードの最終行に1;が含まれる理由

真の値を返すルール
    インクルードしたファイルから呼び出し元に対し、
    コードの処理に成功して、エラー条件が発生してないことを知らせる手段だった
        しかし、実際は、die if ... という方法が使われるようになったため、
        このエラー通知は歴史的な理由で残っている煩わしい習慣になってしまった

requireを使い、Example.pm Example2.pm sample.pl を書き直すと以下のようになる

# Example.pm
sub println
{
    my $message = shift;
    print "$message\n";
}

1;
require 'Example.pm';

sub printlist
{
    my @list = @_;

    for ( @list ) {
        println( $_ );
    }
}

1;
# sample.pl
require 'Example.pm';
require 'Example2.pm';

println( "■ コーヒー豆一覧" );

my @list = qw/
    アメリカンブレンド
    ブルマンブレンド
    マイルドブレンド
    モカブレンド
/;

printlist( @list );

10.5 requireと@INC
これまでの方法は、プログラムとライブラリが同じディレクトリにあり、
そのディレクトリからプログラムを実行するという条件のときのみうまく動作する

ライブラリがカレントディレクトリにない場合
    Perlはサーチパスに沿ってライブラリを検索する

サーチパス
    特殊変数@INCに格納された要素のリスト
        デフォルトでは、以下のディレクトリが含まれる
            ・カレントディレクトリ
            ・Perlコンパイル時に指定したディレクトリ

    @INCの確認方法
        ・perl -V で出力される内容の最後の方
        ・perl -le "print for @INC"

10.5.1 @INCの拡張
マシンのメンテナンス責任者でもない限り、
@INCに設定されたディレクトリにモジュールのインストールはできない
    require文が実行される前に、@INCを書き換えることで、
    モジュールを読み込むディレクトリを追加できる
        unshift @INC, '/home/tyamaguc07/perl-lib';
            unshiftすることで、Perlはまず、/home/tyamaguc07/perl-libからモジュールを探す
                これにより、ファイル間の名前の衝突が起こる場合、
                /home/tyamaguc07/perl-libにあるファイルが優先される

@INCの拡張は、他の何より早く行いたい
    BEGINブロックを使う
        BEGIN { unshift @INC, '/home/tyamaguc07/perl-lib' };

    BEGINブロックを使わない場合
        requireをする前にunshiftを配置しなければならない

    この処理はよく行われる
        Perlはそのためのプラグマ "use lib" を用意している

use lib
    コンパイル時に実行される
    @INCに対して、指定したディレクトリをunshiftする
        use lib qw( /home/tyamaguc07/perl-lib );

標準モジュール FindBinを使うことで、移植性を高める
    use FindBin qw( $Bin ); とすることで、
    $Binにはスクリプトが格納しているディレクトリのパスが格納される
        スクリプトディレクトリのサブディレクトリにライブラリがある場合、
        以下のように、パスを組み立てることができる
            use FindBin qw( $Bin );
            use lib "$Bin/lib";

10.5.2 PERL5LIBを使った@INCの拡張
環境変数 PERL5LIB にプライベートライブラリのディレクトリ名を設定する
    csh系の場合
        setenv PERL5LIB /home/tyamaguc07/perl-lib

    bsh系の場合
        PERL5LIB=/home/tyamaguc07/perl-lib; export PERL5LIB

PERL5LIBは"個人用"には便利

10.5.6 -Iを使った@INCの拡張
PERL5LIBの代わりに、-Iオプションを指定してPerlを実行する
    perl -I/home/tyamaguc07/perl-lib sample.pl

10.6 名前空間の衝突の問題
複数のライブラリで、同じサブルーチン名でサブルーチンが定義されている場合、
先にrequireされたライブラリのサブルーチンは、
後からrequireされたモジュールのサブルーチンで再定義されてしまう
    サブルーチンだけではなく、各種変数にも起こりえる

再定義を防ぐ方法
    各種変数、サブルーチン名に一意になるようなプレフィックスを付ける
        この方法は、変数名が長くなるなどあまり美しくない

10.7 名前空間のセパレータとしてのパッケージ

プレフィックスを付ける代わりに、packageを使う

# Example.pm
package Example;    # パッケージ宣言

sub printlist
{
    my @list = @_;

    for ( @list ) {
        println( $_ );
    }
}

sub println
{
    my $message = shift;
    print "$message\n";
}

1;
package宣言
    ファイル内のほとんどの名前の前に、Example::を挿入することと実質的に同じ意味
        上記のコードは以下のような意味となる
# Example.pm
sub Example::println
{
    my @list = @_;

    for ( @list ) {
        println( $_ );
    }
}

sub Example::printlist
{
    my $message = shift;
    print "$message\n";
}

1;
このライブラリを使うとき...
    ライブラリ内で定義されたサブルーチンにはExample::を付けアクセスする

    自身で定義したサブルーチンには、何も付けずにアクセスする
# sample.pl
require 'Example.pm';

sub printhash
{
    my %hash = @_;
    Example::printlist( map { "$_ : $hash{$_}" } sort keys %hash );
}

printhash(
    'アメリカンブレンド'    =>  420,
    'ブルマンブレンド'      =>  700,
    'マイルドブレンド'      =>  420,
    'モカブレンド'          =>  420,
);
パッケージ名
    変数名と似ている
        ・英数字と下線を使うことはできる
        ・先頭を数字にすることはできない

    perlmodlibで説明されている理由から
        ・パッケージ名の先頭文字は大文字
        ・既存のCPANモジュールやコアモジュール名と重複しないようにする

    二重のコロンで区切って複数の名前を入れることも可能
        例えば、Example::Print

    ほとんどの変数名やサブルーチン名には、
    カレントパッケージの名前がプレフィックスとして付けられる
        ただし、名前に二重コロンが含まれない場合

        パッケージ内で定義された変数にも、
        メインプログラムでパッケージ名を付けることでアクセスできる

    すべてのプログラムは、mainという名前のパッケージに含まれる
        ファイルの冒頭にpackage main; という文を記述した場合と同じ

10.8 packageディレクティブのスコープ
すべてのファイルの冒頭には、package main; と書かれていると考えることができる
    packageディレクティブは、次のpackageディレクティブまで有効

    ただし、次のpackageディレクティブが、ブレースの中にある場合は異なる
        スコープの終了とともに、元のpackageディレクティブが有効になる

        カレントパッケージは、レキシカルスコープ
package Example;

{
    package main;   # ここからパッケージはmain

    sub printhash
    {
        ...
    }
}

# パッケージはExampleに戻る
sub println
{
    ...
}

10.9 パッケージとレキシカル変数
パッケージ変数は常にグローバル変数
    完全名を知っていれば、いつでも参照可能

    レキシカル変数には、カレントパッケージのプレフィックスは付かない
        レキシカル変数にアクセスしたい場合、パッケージプレフィックスを付けない

        パッケージ変数にアクセスしたい場合、パッケージプレフィックスを付ける
# Example.pm
package Example;

@list = qw/ 1 2 3 4 /;

sub printlist
{
    my @list = qw/ a b c d /;

    print map { "$_\n" } @list;             # レキシカル変数を参照
    print map { "$_\n" } @Example::list;    # パッケージ変数を参照
}

sub list
{
    return @list;   # パッケージ変数を参照
}

1;
# sample.pl
require 'Example.pm';

Example::printlist();
print Example::list();

=comment
実行すると以下のような出力が得られる
perl sample.pl
a
b
c
d
1
2
3
4
1234

感想

細かい仕様については把握出来ていなかったな。

知ることができてよかった。

そして、11章が楽しみ。