5

パッケージの名前を表す文字列を含む変数が与えられた場合、パッケージの特定のサブルーチンを呼び出すにはどうすればよいですか?

これが私が理解した最も近いものです:

package MyPackage;

sub echo {
    print shift;
}

my $package_name = 'MyPackage';
$package_name->echo('Hello World');

1;

このコードの問題は、サブルーチンがクラス メソッドとして呼び出されることです。パッケージ名は最初の引数として渡されます。特別な最初の引数が暗黙的に渡されることなく、パッケージ名からサブルーチンを呼び出したい。

4

3 に答える 3

8

実際にメソッドとして呼び出すのではなく、通常のサブルーチンとして呼び出したいようです。その場合、シンボリック参照を使用できます。

my $package_name = 'MyPackage';
{ 
    no strict 'refs';
    &{ $package_name . '::echo' }( 'Hello World' );
}
于 2012-06-05T02:06:24.470 に答える
4

Perlメソッド呼び出しは単なる通常のサブルーチンであり、最初の値として呼び出し元を取得します。

use strict;
use warnings;
use 5.10.1;

{
  package MyPackage;
  sub new{ bless {}, shift } # overly simplistic constructor (DO NOT REUSE)
  sub echo{ say @_ }
}

my $package_name = 'MyPackage';
$package_name->echo;

my $object = $package_name->new();
$object->echo; # effectively the same as MyPackage::echo($object)
MyPackage
MyPackage=HASH(0x1e2a070)

呼び出し元なしでサブルーチンを呼び出したい場合は、別の方法で呼び出す必要があります。

{
  no strict 'refs';
  ${$package_name.'::'}{echo}->('Hello World');
  &{$package_name.'::echo'}('Hello World');
}

# only works for packages without :: in the name
$::{$package_name.'::'}{echo}->('Hello World');

$package_name->can('echo')->('Hello World');
  • このcanメソッドは、呼び出し元で呼び出された場合に呼び出されるサブルーチンへの参照を返します。その後、coderefを個別に使用できます。

    my $code_ref = $package_name->can('echo');
    $code_ref->('Hello World');
    

    使用する際の注意点がいくつかありますcan

    • canパッケージ、またはパッケージが継承する任意のクラスによってオーバーライドされる場合があります。
    • メソッドを定義するパッケージは、呼び出し元とは異なる場合があります。


    これは実際にはあなたが探している振る舞いかもしれません。

  • 別のアプローチは、シンボリックリファレンスと呼ばれるものを使用することです。

    {
      no strict 'refs';
      &{ $package_name.'::echo' }('Hello World');
    }
    

    通常、シンボリック参照の使用はお勧めしません。問題の一部は、シンボル参照を使用するつもりがなかった場所で誤って使用する可能性があることです。これがuse strict 'refs';、効果を発揮できない理由です。

    これはあなたがやりたいことをする最も簡単な方法かもしれません。

  • シンボリック参照を使用したくない場合は、Stashを使用できます。

    $MyPackage::{echo}->('Hello World');
    $::{'MyPackage::'}{echo}->('Hello World');
    
    $main::{'MyPackage::'}{echo}->('Hello World');
    $main::{'main::'}{'MyPackage::'}{echo}->('Hello World');
    $main::{'main::'}{'main::'}{'main::'}{'MyPackage::'}{echo}->('Hello World');
    

    これに関する唯一の問題は、分割する必要があるということ$package_nameです::

    *Some::Long::Package::Name::echo = \&MyPackage::echo;
    
    $::{'Some::'}{'Long::'}{'Package::'}{'Name::'}{echo}('Hello World');
    
    sub get_package_stash{
      my $package = shift.'::';
      my @package = split /(?<=::)/, $package;
      my $stash = \%:: ;
      $stash = $stash->{$_} for @package;
      return $stash;
    }
    get_package_stash('Some::Long::Package::Name')->{echo}('Hello World');
    

    しかし、これはそれほど大きな問題ではありません。CPANをざっと見てみると、 Package::Stashが見つかります。

    use Package::Stash;
    my $stash = Package::Stash->new($package_name);
    my $coderef = $stash->get_symbol('&echo');
    $coderef->('Hello World');
    

    PurePerlバージョンのPackage:: Stashは、Stashではなくシンボリック参照を使用します)


Exporterを使用していたモジュールからインポートされたかのように、サブルーチン/メソッドのエイリアスを作成することも可能です。

*echo = \&{$package_name.'::echo'};
echo('Hello World');

ただし、エイリアスの範囲を制限することをお勧めします。

{
  local *echo = \&{$package_name.'::echo'};
  echo('Hello World');
}

これは例外であり、strict 'refs'有効にした状態でシンボリック参照を使用できます。

于 2012-06-05T02:40:14.220 に答える
1

逆参照演算子をリストするときに&{ <EXPRESSION> }()説明したように、名前が式であるサブルーチンを呼び出す構文を使用します。perldoc perlref

確かに、この場合にカーリーを使用するのは少しばかげていますが、BLOCK には任意の式、特に添字式を含めることができます。

  &{ $dispatch{$index} }(1,2,3); # call correct routine

ランダムな実際の例:

# Note no "use strict"!
use File::Slurp; 
my $p="File::Slurp"; 
@a=&{"${p}::read_file"}(".profile"); 
print $a[0];
于 2012-06-05T02:07:36.210 に答える