2011年1月29日

PerlのGetopt::LongモジュールにはGetOptionsFromArrayという関数が有る

Getopt::Longは、@ARGVを解析して、コマンドラインオプションを抜き出してくれる標準モジュール。コマンドラインオプション以外の引数は、そのまま@ARGVに残る(ファイル名とかね)。

試しにこんなコードを書いてみます。
getopt1.pl

#!/usr/bin/env perl

use strict;
use warnings;

use Getopt::Long;

{
        local @ARGV = @ARGV;

        my $option_value;
        GetOptions("o|option=s" => \$option_value);

        if ($length) {
                print "Command line option is $option_value.\n";
        }

        print 'Current local @ARGV is ';
        print "@ARGV.\n";
}

print 'Orignal @ARGV is ';
print "@ARGV.\n";

実行すると、結果はこうなります。

$ perl getopt1.pl filename -o test_option
Command line option is test_option.
Current local @ARGV is filename.
Orignal @ARGV is filename -o test_option.

グローバル変数である@ARGVをいきなり書き換えるのはちょっとお行儀が悪いので、localで再定義した@ARGVで受ける様に書きましたが、いちいちlocal @ARGV=@ARGVというコードを書くのも、若干回りくどいし、分かりづらいですよね(グローバル変数を隠蔽するlocal変数はみんな分かりづらいコードの原因になります...)。

最近のGetopt::Longモジュール(Ver.2.36以降)には、任意の配列からオプションを取得するGetOptionsFromArray関数が追加されています。

GetOptionsFromArrayを使って書き換えると、下の様になります。

getopt2.pl

#!/usr/bin/env perl

use strict;
use warnings;

use Getopt::Long qw(GetOptionsFromArray);

{
        my @args = @ARGV;

        my $option_value;
        GetOptionsFromArray(\@args, "o|option=s" => \$option_value);

        if ($option_value) {
                print "Command line option is $option_value.\n";
        }

        print 'Current @args is ...';
        print "@args.\n";
}

print 'Orignal @ARGV is ...';
print "@ARGV.\n";

実行結果は下の様になります。

$ perl getopt2.pl filename -o test_option
Command line option is test_option.
Current @args is ...filename.
Orignal @ARGV is ...filename -o test_option.

当たり前ですが、同じ結果が表示されました。

コードが全部一つの.plに入っているようなコードでは直接@ARGVを書き換えてしまっても、あまり影響は無いと思いますが、なるべくグローバル変数を使わない(更に勝手に書き換えない)というのは大事なお作法なので、この様なコードの書き方をしたいところです。

でも残念な事に、Getopt::LongのVer.2.36が標準モジュール入りしたのはPerl 5.8.9以降(リリース順で言えば、Perl 5.10.0以降)なので、Perlのバージョンによっては最新バージョンをCPANからインストールする必要が有ります。

$ corelist -a Getopt::Long

Getopt::Long was first released with perl 5
  5          undef
  v5.10.0    2.37
  v5.8.0     2.32      
  v5.8.1     2.34      
  v5.8.2     2.34      
  v5.8.3     2.34      
  v5.8.4     2.34      
  v5.8.5     2.34      
  v5.8.6     2.34      
  v5.8.7     2.34      
  v5.8.8     2.35      
  v5.8.9     2.37      
  v5.9.0     2.34      
  v5.9.1     2.3401    
  v5.9.2     2.3401    
  v5.9.3     2.35      
  v5.9.4     2.3501    
  v5.9.5     2.36      
  v5.10.0    2.37
(表示は一部省略しています)

地味に進化しているGetopt::Longの関数紹介でした。

2011年1月26日

Perl 5.12.3リリース

Perl 5.12.3がリリースされています。

Download Perl - www.perl.org

perlbrewをインストールしている環境だったら、たった二つのコマンドでバージョンアップできます。

$ perlbrew install perl-5.12.3
$ perlbrew switch perl-5.12.3

変更点はリリースノートを見ましょう。小さなバグ修正と、ドキュメントの修正ぐらいですね。5.12.0から使える、組み込み関数のkeys, values, eachが配列にも使えるようになった点がドキュメントに追加されたそうです。

で、この機能のメリットって何だろう...。

追記

リリースノートが翻訳されていました。早い!

2011年1月15日

PerlのUNIVERSALモジュールのcanメソッドを使ったコマンドディスパッチャー

Perlのオブジェクト(blessされたreference)は、全てUNIVERSALモジュールを継承しています。UNIVERSALモジュールは非常に便利な三つのメソッドを提供しています。

オブジェクトのクラスを判定するisa、クラスのバージョンを取得するversion、オブジェクトが実行できるメソッドを判定するcanの三つです。

canメソッドを使うと、そのオブジェクトが引数で渡したメソッドを起動できるか判定でき、起動できる時はメソッドへのコードリファレンスを返却してくれます。

#!/usr/bin/env perl

package test_class;

use strict;
use warnings;

sub new {
	my $class = shift;

	my $self = { };

	return bless $self, $class;
}

sub Output_Message {
	my $self = shift;

	print "Method Message...\n";
}

package main;
use strict;
use warnings;

my $app = test_class->new;

my $method = $app->can("Output_Message");

if ($method) {
	$app->$method;
}

かなりどうでもいいコードですが、このコードを実行すると、「Method Message...」と表示されます。

$ perl can_test.pl
Method Message...

ポイントはmainパッケージの中で、"test_class"のオブジェクトに対して、canメソッドで「Output_Message」メソッドが使えるかどうかを確認しているところです。使えなければundef、使えればメソッドのコードリファレンスが返却されるので、そのままコードリファレンスを元にメソッドを起動しています。

この仕組みを使えば、例えば引数でコマンド名を受け取る様なツールを作る時に、コードの中にコマンドのディスパッチャーをいちいち対応するコマンドごとにif文で判定する様なコードを書く必要が有りません。

package App::Dispatch;

use strict;
use warnings;

sub new {
	my $class = shift;

	my $self = {};

	return bless($self, $class);
}

sub cmd_install {
	my $self = shift;

	print "Install ...";
}

sub cmd_switch {
	my $self = shift;

	print "Switch ...";
}

sub run {
	my $self = shift;
	my $command = shift;

	my $method = $self->can("cmd_" . $command);

	if ($method) {
		$self->$method;
	} else {
		print "Incorrect command...$command\n";
	}
}

コマンド名をそのままメソッド名にすると、内部のメソッドまで偶然に呼び出される可能性が出てしまうので、何かコマンドを表す修飾名をメソッド名には付けた方が良いでしょう。

こうすることで、コマンドが増えるたびにメソッドのディスパッチのコードに、コマンド名とメソッド名を判定するロジックを書かなくても済みます。

2011年1月 5日

Parse::CPAN::Metaを使って、META.ymlをパースする

CPANモジュールには必ず含まれるMETA.ymlには、モジュールのビルド条件などが書かれていて、cpanコマンドなど様々なツールでモジュール情報を取得するために使われています。

ちなみにMETA.ymlの仕様というか、歴史はCPAN::Meta::Historyで読むことができます。ちなみに、これを読むと2010年4月にJSON形式の仕様書がリリースされています。

さて、META.ymlから依存モジュールを抜き出すコードをYAML::Tinyを使って書いていたのですが、Perl-5.10.1から標準モジュール入りしたParse::CPAN::Metaを使えば一発だと一日経ってから気がつきました。

ファイル名を指定して「LoadFile」メソッドを使えば、すぐに中身を取り出すことができます。

試しにこんなコードを書いてみました。

#!/usr/bin/env perl

# meta.pl

use strict;
use warnings;

use Parse::CPAN::Meta qw(LoadFile);

my $yaml = LoadFile('META.yml');

use Data::Dumper;
print Dumper($yaml);

例えば、JSONモジュールのMETA.ymlをパースすると、こんな結果が得られます。

$ perl meta.pl
$VAR1 = {
          'no_index' => {
                          'directory' => [
                                           't',
                                           'inc'
                                         ]
                        },
          'resources' => {
                           'repository' => 'http://github.com/makamaka/JSON'
                         },
          'meta-spec' => {
                           'version' => '1.4',
                           'url' => 'http://module-build.sourceforge.net/META-spec-v1.4.html'
                         },
          'generated_by' => 'ExtUtils::MakeMaker version 6.56',
          'distribution_type' => 'module',
          'version' => '2.50',
          'name' => 'JSON',
          'author' => [
                        'Makamaka Hannyaharamitu, Emakamaka[at]cpan.orgE'
                      ],
          'license' => 'perl',
          'build_requires' => {
                                'ExtUtils::MakeMaker' => '0'
                              },
          'requires' => {
                          'Test::More' => '0'
                        },
          'abstract' => 'JSON (JavaScript Object Notation) encoder/decoder',
          'configure_requires' => {
                                    'ExtUtils::MakeMaker' => '0'
                                  }
        };

簡単ですね。一日頑張って無駄になりました。なお、開発中のバージョン1.41ではMETA.json形式も使えるようになるそうです。

2011年1月 3日

PerlのArchive::Extractモジュールが便利

Perl-5.10.0から標準モジュール入りしたArchive::Extractが便利。

tarや、tgz(tar.gz)、zip等のアーカイブファイルを展開する時に、拡張子を判定して、適切な展開プログラムを選択してくます。

使い方は簡単です。アーカイブファイルのパスを渡して、Archive::Extractのオブジェクトを生成して、解凍先ディレクトリを指定して、解凍するためのメソッドを呼び出すだけです。

use Archive::Extract;
my $ae = Archive::Extract->new(archive => '/path/to/file');
my $ok = $ae->extract(to => '/path/to/extract_dir') or $self->_croak($ae->error);

あとは拡張子を判定して、解凍してくれます。解凍先ディレクトリを指定しない場合は、カレントディレクトリへ展開されます。

2011年1月 1日

Perlの書籍を、用意する

一時期はひじょうにたくさん出版されていたPerl関係の書籍ですが、"CGIで掲示板を作ろう"的な物が多く、元々のPerlの得意分野であるテキスト処理やサーバ管理のユーティリティ、イマドキのWebアプリケーション構築等では参考にならない本がほとんどです。また、Web上の情報と同じように古い情報に基づいた本が多いです。

今でも使えるお勧めの書籍を挙げてみます。

  • Learning Perl(邦題「初めてのPerl」)
  • Perl入門書の定番中の定番。Perl 5.10以降に対応した本です。Perlの文法を理解する上でマストの一冊です。丁寧な言語機能の紹介と共に練習問題が付いていて、自分の理解度を確認することができます。また、敢えて全ての機能を紹介するのではなく、最低限必要な機能に絞っていて、逆に何を紹介していないのかをきちんと説明しているところが、この本以降へのステップアップ方法を示してくれます。

    特に正規表現まわりの丁寧な解説はどの書籍よりもお勧めです。

    初めてのPerl 第5版
    初めてのPerl 第5版
    Learning Perl
    Learning Perl

  • Intermediate Perl(邦題「続・初めてのPerl」)
  • 「初めてのPerl」では触れられていないリファレンス、Perl独特のオブジェクト指向、そしてテストコードの書き方などを扱っています。他人が書いたモジュールを読む上ではマストとなる一冊で、「初めてのPerl」を読み終わった後には必ず読む事をお勧めします。

    続・初めてのPerl 改訂版
    続・初めてのPerl 改訂版
    Intermediate Perl
    Intermediate Perl

  • Perl Cookbook(邦題「Perlクックブック」
  • Perlのサンプルコード集の定番中の定番。単にコードの断片を載せるだけでなく、なぜその方法を使うのか、根拠も含めて丁寧に解説されています。

    原著が出版されたのが2003年なので、そろそろ紹介されているコードも古くなってきたものも有りますが(CPANモジュールが新しい定番が出てきていたり、Perl本体の機能も変わっていたり)、基本はちゃんと押さえてあるので、今でも現役の一冊です(邦訳は2分冊ですが、Volume1が有ればたいていのことは大丈夫です)。

    Perlクックブック〈VOLUME1〉
    Perlクックブック〈VOLUME1〉
    Perlクックブック〈VOLUME2〉
    Perlクックブック〈VOLUME2〉
    Perl Cookbook
    Perl Cookbook

  • モダンPerl
  • 決して入門書というか、初心者の人が読む本ではないのですが、近年出版されたPerl関係の書籍の中で最も良書と言える一冊。中級者の人が必要になるトピック、かつイマドキのPerlとして重要なテクニックが多数紹介されていて、まさに「モダンPerl」を理解するための基本と言えるでしょう。

    モダンPerl入門 (CodeZine BOOKS)
    モダンPerl入門 (CodeZine BOOKS)

  • Programming Perl(邦題「プログラミングPerl」)
  • Perl生みの親、Larry WallによるPerlというプログラミング言語の解説書。原典。

    とはいえ、いきなり読むにはあまりに敷居が高く、Perl 5.6時代に書かれた情報もちょっと古くなってきたところ...。Perlを極めたい!という人以外には勧めないが、機会とお金が有る人はぜひ読んで欲しい一冊です。

    プログラミングPerl〈VOLUME1〉
    プログラミングPerl〈VOLUME1〉
    プログラミングPerl〈VOLUME2〉
    プログラミングPerl〈VOLUME2〉
    Programming Perl
    Programming Perl