2012-06-05 56 views
5

給定一個包含表示包名稱的字符串的變量,如何調用該包的特定子例程?如何從包中調用子程序,給定Perl中的包名?

這是我想通了最接近的事:

package MyPackage; 

sub echo { 
    print shift; 
} 

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

1; 

這段代碼的問題是子程序稱爲一個類的方法;包名稱作爲第一個參數傳入。我想從包名稱中調用子程序,而沒有特殊的第一個參數被隱式傳入。

+1

如果你不想傳遞一個invocant,那麼你想要的不是調用*方法*。 – hobbs

+0

@hobbs:謝謝。我已經更新了該問題以使用更正確的術語。 – Sam

回答

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可以由封裝覆蓋,或從它繼承的任何類。
    • 定義方法的包可能與調用者不同。


    這實際上可能是您正在尋找的行爲。

  • 另一種方法是使用稱爲symbolic reference的東西。

    { 
        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'); 
    

    (該Pure Perl版本的Package::Stash使用符號引用,而不是藏匿)


它甚至可能使例程/方法的別名,彷彿已經從進口一個正在使用的模塊Exporter

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

我會r ecommend限制別名的範圍,但:

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

這是一個例外,在那裏你可以使用啓用strict 'refs'一個象徵性的參考。

+0

感謝您的全面解答。我已經接受了它,因爲你提供了多種方法來完成我所要求的。我特別喜歡'可以'的方法。 – Sam

+1

@Sam,'can'是錯誤的。它檢查繼承樹。 'can'是爲了方法。 – ikegami

8

聽起來你不想將它實際稱爲方法,而是作爲常規子程序。在這種情況下,你可以使用一個符號引用:

my $package_name = 'MyPackage'; 
{ 
    no strict 'refs'; 
    &{ $package_name . '::echo' }('Hello World'); 
} 
+0

+1爲「沒有嚴格的參考」;「精度 – DVK

+2

如果您使用'$ :: {$ package_name。'::'} {echo}',則不需要使用'no strict'refs';' –

1

使用&{ <EXPRESSION> }()語法調用子,其名稱是表達的,上市非關聯經營者,當爲perldoc perlref討論:

誠然,這是有點傻使用花括號在這種情況下,但塊可以包含任意表達,特別地,下標表達式:

&{ $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]; 
相關問題