@INC からモジュール探し

前回の perlwhere に対して、id:dankogai さんにフォローをしていただきました。ありがとうございます。

404 Blog Not Found:perl - モジュールと%INC
まず、モジュールのありかを手っ取り早く調べる方法としては、

% perldoc -ml

が使える。PODが含まれてなくてもOK。

Perlスクリプトの中でモジュールのありかを調べるのにも使えるようにと考えていて前のエントリになったのですが、前提を書き忘れていました。ご指摘の通り、単にありかをコマンドラインから調べるためだけなら、perldoc で充分です。書いてなかった意図を察していただいており、恐縮します。
さて、そのためには、前回は Module::Build::ModuleInfo を使えば楽だと書いたのですが、今回はこれを使わないで調べる方法を id:dankogai さんにならってやってみます。

しかし、中級以上の perl monger を目指すのであれば、%INCのことも知っておきたい。

で、id:dankogai さんはモジュールのありかを require して %INC で調べる方法を紹介していらっしゃいますけど、この方法は一つ欠点があります。require したモジュールが use している他のモジュールもかたっぱしから一気にロードしてコンパイルしてしまうことです。なんとも富豪的
では、perldoc や、Module::Build::ModuleInfo がどういう処理をしているのだろうかと、ソースを読んでみると、両方とも @INC の各ディレクトリ名へモジュールのファイル名をくっつけつつ、ファイルが存在しているかどうかをチェックして、最初に見つかったものを採用していました。
真似をしてみましょう。ここでは処理を簡略化して、拡張子が pm のファイルを探すことにします。File::Spec を使ってモジュール名からファイル名に変換するやりかたは、Module::Build::ModuleInfo からぱくりました。

#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use List::Util qw(first);

print "$_\n" for map { module_to_filename($_) } @ARGV;

sub module_to_filename {
    my $file = File::Spec->catfile(split /::/, shift) . '.pm';
    first { -f $_ && -r _ }
        map { File::Spec->rel2abs(File::Spec->catfile($_, $file)) } @INC
    or ();
}

List::Util の first は、条件に合致するものが見つからなかったときに、なぜか undef を返すので、空リストにすげかえました。
でも、この程度のことに List::Util を使うまでもないということで、for ループ版に書き直しました。

#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;

print "$_\n" for map { module_to_filename($_) } @ARGV;

sub module_to_filename {
    my $file = File::Spec->catfile(split /::/, shift) . '.pm';
    for my $dir (@INC) {
        my $path = File::Spec->rel2abs(File::Spec->catfile($dir, $file));
        return $path if -f $path && -r _;
    }
    ();
}

でも、List::Util を使う方が何をやっているのか一目で読み取れて好みかな。