2

モジュールが使用前にその内容を読み取れるように、実行時に必要なデータ ファイルを Perl モジュールにバンドルする「適切な」方法は何ですか?

簡単な例は、この辞書モジュールで、起動時に (単語、定義) ペアのリストを読み取る必要があります。

package Reference::Dictionary;

# TODO: This is the Dictionary, which needs to be populated from
#  data-file BEFORE calling Lookup!
our %Dictionary;

sub new {
  my $class = shift;
  return bless {}, $class;
}

sub Lookup {
  my ($self,$word) = @_;
  return $Dictionary{$word};
}
1;

およびドライバー プログラム Main.pl:

use Reference::Dictionary;

my $dictionary = new Reference::Dictionary;
print $dictionary->Lookup("aardvark");

今、私のディレクトリ構造は次のようになります。

root/
  Main.pl
  Reference/
    Dictionary.pm
    Dictionary.txt

起動時に Dictionary.pm に Dictionary.txt をロードさせることができないようです。これを機能させるために、次のようないくつかの方法を試しました...

  • BEGIN ブロックの使用:

    BEGIN {
      open(FP, '<', 'Dictionary.txt') or die "Can't open: $!\n";
      while (<FP>) {
        chomp;
        my ($word, $def) = split(/,/);
        $Dictionary{$word} = $def;
      }
      close(FP);
    }
    

    ダイスなし: Perl は cwd で Dictionary.txt を探します。これはメイン スクリプト ("Main.pl") のパスであり、モジュールのパスではないため、File Not Found と表示されます。

  • データの使用:

    BEGIN {
      while (<DATA>) {
        chomp;
        my ($word, $def) = split(/,/);
        $Dictionary{$word} = $def;
      }
      close(DATA);
    }
    

    そしてモジュールの最後に

    __DATA__
    aardvark,an animal which is definitely not an anteater
    abacus,an oldschool calculator
    ...
    

    DATAが利用可能になる前に、コンパイル時に BEGIN が実行されるため、これも失敗します。

  • モジュール内のデータをハードコーディングする

    our %Dictionary = (
      aardvark => 'an animal which is definitely not an anteater',
      abacus => 'an oldschool calculator'
      ...
    );
    

    動作しますが、明らかに保守できません。

同様の質問: Perl モジュールでデータ ファイルを配布するにはどうすればよいですか? しかし、それは、私がやろうとしている現在のスクリプトに関連するモジュールではなく、CPAN によってインストールされたモジュールを扱います。

4

2 に答える 2

5

辞書を一度にロードする必要はありませんBEGINBEGIN時間は、ロードされているファイルに関連しています。とmain.pl言うとuse Dictionary、Dictionary.pm 内のすべてのコードがコンパイルされてロードされます。Dictionary.pm の早い段階でロードするコードを配置します。

package Dictionary;

use strict;
use warnings;

my %Dictionary;  # There is no need for a global
while (<DATA>) {
    chomp;
    my ($word, $def) = split(/,/);
    $Dictionary{$word} = $def;
}

Dictionary.txt同じディレクトリにあるからロードすることもできます。トリックは、ファイルへの絶対パスを提供する必要があることです。__FILE__これは、現在のファイルへのパス (つまり)から取得できますDictionary.pm

use File::Basename;

# Get the directory Dictionary.pm is located in.
my $dir = dirname(__FILE__);

open(my $fh, '<', "$dir/Dictionary.txt") or die "Can't open: $!\n";

my %Dictionary;
while (<$fh>) {
    chomp;
    my ($word, $def) = split(/,/);
    $Dictionary{$word} = $def;
}
close($fh);

どちらを使用する必要がありますか? DATAより簡単に配布できます。別の並列ファイルは、コーダー以外の人が作業するのが簡単です。


ライブラリがロードされるときに辞書全体をロードするよりも、必要なときにロードするのを待つ方が丁寧です。

use File::Basename;

# Load the dictionary from Dictionary.txt
sub _load_dictionary {
    my %dictionary;

    # Get the directory Dictionary.pm is located in.
    my $dir = dirname(__FILE__);

    open(my $fh, '<', "$dir/Dictionary.txt") or die "Can't open: $!\n";

    while (<$fh>) {
        chomp;
        my ($word, $def) = split(/,/);
        $dictionary{$word} = $def;
    }

    return \%dictionary;
}

# Get the possibly cached dictionary
my $Dictionary;
sub _get_dictionary {
    return $Dictionary ||= _load_dictionary;
}

sub new {
    my $class = shift;

    my $self = bless {}, $class;
    $self->{dictionary} = $self->_get_dictionary;

    return $self;
}

sub lookup {
    my $self = shift;
    my $word = shift;

    return $self->{dictionary}{$word};
}

各オブジェクトには、オブジェクトの作成時にのみロードされる共有ディクショナリへの参照が含まれるようになりました (グローバルの必要性がなくなりました)。

于 2015-11-05T02:51:46.683 に答える
0

実行前にデータが初期化されるようにする代わりにDATAwithを使用することをお勧めします。また、より自己文書化しますINITBEGIN

または、ブロックを使用する方が適切な場合がありますUNITCHECK。このブロックは、ライブラリ ファイルがコンパイルされた直後にできるだけ早く実行されるため、コンパイルの拡張と見なすことができます。

package Dictionary;

use strict;
use warnings;

my %dictionary;
UNITCHECK {
    while ( <DATA> ) {
        chomp;
        my ($k, $v) = split /,/;
        $dictionary{$k} = $v;
    }
}
于 2015-11-05T03:09:32.177 に答える